SWJTU OJ 西安交通大学第十三届ACM程序设计决赛-题解

106 阅读1分钟

A Knapsack Problem(线性时间复杂度 签到题)

#include <stdio.h>
int main()
{
	int T;
	scanf("%d", &T);
	while(T-- > 0) {
		int n, w, d, i, tmp;
		scanf("%d%d%d", &n, &w, &d);
		for (i= 0; i < n; ++i) {
			scanf("%d", &tmp);
			w -= tmp;
		}
		for (i = 0; i < n; ++i) {
			scanf("%d", &tmp);
			d -= tmp;
		}
		if (w >= 0 && d >= 0) {
			puts("YES");
		}
		else {
			puts("NO");
		}
	}
	
	return 0;
} 


B Matrix
  O(n * n + q)

(r[k] = v意为第k行被置为v;c[k] = v,意为k列被置为v,rt[k] = i 意为修改第k行的值的时间为“第i次操作”, ct[k] = i 意为修改第k列的时间为“第i次操作”, matrix[i][j]的值为最近对行列操作给的值

#include <cstdio>
#include <iostream>
#include <algorithm> 
#include <cstring>
#include <string>
#include <climits>
#include <cmath>
using namespace std;
const int maxn = 5e2 + 7;
int n, q;
int matrix[maxn][maxn];
int r[maxn], c[maxn], rt[maxn], ct[maxn];
int main()
{
	int T;
	scanf("%d", &T);
	while(T-- > 0) {
		scanf("%d%d", &n, &q);
		//memset(matrix, 0, sizeof(int)*maxn * maxn);
		memset(r, 0, sizeof(int) * maxn);
		memset(c, 0, sizeof(int) * maxn);
		memset(rt, 0, sizeof(int) * maxn);
		memset(ct, 0, sizeof(int) * maxn);
		for (int i = 1; i <= q; ++i) {
			int op, k, v;
			scanf("%d%d%d", &op, &k, &v);
			if (op == 1) {
				r[k] = v;
				rt[k] = i;//记录修改的时间 
			}
			else if (op == 2) {
				c[k] = v;
				ct[k] = i; 
			}
		}
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (rt[i] > ct[j]) { //它们是不可能相等的 
					matrix[i][j] = r[i]; 
				}
				else {
					matrix[i][j] = c[j];
				}
			}
		}
		for (int i = 1; i <= n; ++i) {
			printf("%d", matrix[i][1]);
			for (int j = 2; j <= n; ++j){
				printf(" %d", matrix[i][j]);
			}
			puts("");
		}
	} 
	
	return 0;
} 

\

D Music Problem Music Problem O(n * mod)

DP,优化了一下空间(用两个一维向量代替了矩阵),用鸽巢原理做了一下简单的优化。

就是中一些数字,使它们的和sum可以被一个数mod整除。余数最多有mod个(0:mod - 1),求出所有的余数。有余数0就说明有解。

#include <stdio.h>
#include <string.h>
#define MOD 3600
#define MAXN 100007
int ary[MAXN];
char est[MOD + 7], i_est[MOD +7];
int main()
{
	int T;
	scanf("%d", &T);
	while(T-- > 0) {
		memset(est, 0, sizeof(char) * (MOD + 7));
		memset(i_est, 0, sizeof(char) * (MOD + 7));
		int n, i, j;
		scanf("%d", &n);
		for (i= 0; i < n; ++i) {
			scanf("%d", ary + i);
		}
		if (n > 3600) {
			puts("YES");
			continue;//“组合数学”中的鸽巢原理 
		}
		else {
			for (i = 0; i < n; ++i) {
				for (j = 1; j < MOD; ++j) {
					if (est[j] == 1) {
						i_est[(j + ary[i]) % MOD] = 1;
					}					 
				}
				i_est[ary[i] % MOD] = 1;
				if (i_est[0] == 1) {
					est[0] = 1;
					break;
				}
				memcpy(est, i_est, sizeof(char) * (MOD + 7));				
			}
			if (est[0]) {
				puts("YES");
			}
			else {
				puts("NO");
			}
		} 
	}	
	
	return 0;
} 

\

F Maximize The Beatiful Value O(N)

由于序列非递减,所以第i个数向左刚好移动k步美丽值减少的最少。

先用pre数组保存a[]序列的前缀和。sum 为不移动时的美丽值。

将a[i] 移动到a[i - k - 1]时,a[i - k - 1], a[i -k], a[i - k + 1], ..., a[i - 1] 被向右挤了一个位置。美丽值增加了它们的和(即pre[i - 1] - pre[i - k - 1]),但减少了k 倍的a[i](因为a[i] 左移k位,权值由i变为了i - k)。

总的来说:美丽值增加了 pre[i - 1] - pre[i - k - 1] - a[i] * k (为负值,想一想,为什么?)

#include <cstdio>
#include <algorithm> 
#include <climits> 
using namespace std;
const int maxn = 1e5 + 7;
long long ary[maxn], pre[maxn];
int main()
{
	int T;
	scanf("%d", &T);
	while(T--) {
		int n, k;
		long long sum = 0;
		scanf("%d%d", &n, &k);
		pre[0] = 0;
		for (int i= 1; i <= n; ++i) {
			scanf("%d", ary + i);
			pre[i] = pre[i - 1] + ary[i];
			sum += ary[i] * i;
		} //得到初始的魅力值 
		long long minV = LLONG_MAX; //减少的最小值 
		for (int i = k + 1; i <= n; ++i) {
			minV = min(minV, -(pre[i - 1] - pre[i - k - 1] - ary[i] * k));
		}
		printf("%lld\n", sum - minV); 
	}	
	
	return 0;
} 


\

K Segment Tree  O(n)

前缀和presum[i]保存1到i出现的次数,线性时间复杂度,不用暴力就可以

#include <stdio.h>
#include <string.h>
const int maxn = 1e2 + 7;
int ary[maxn];
int presum[maxn];
int main()
{
	int T;
	scanf("%d", &T);
	while(T-- > 0 ) {
		int n, k, d, tmp, cnt = 0;
		memset(ary, 0, sizeof(int) * maxn);
		scanf("%d%d%d", &n, &k, &d);
		for (int i= 1; i <= n; ++i) {
			scanf("%d", &tmp);
			ary[tmp]++;
		}
		presum[0] = 0;
		for (int i = 1; i <= n; ++i) {
			presum[i] = presum[i - 1] + ary[i];
		} //前缀和,保存(1:i)出现次数 
		int End = n - d + 1;
		for (int i = 1; i <= End; ++i) {
			int R = i+ k - 1; 
			if (R > n) {
				R = n;
			}
			if (presum[R] - presum[i - 1] >= d) {
				cnt++;
			}
		}
		printf("%d\n", cnt);
	}	
	
	return 0;
} 


\

\

\

=========未完待续=======

\

by wjsay

\


\

\

\