第一题 The Castle
算法:求连通分量的个数
题目给出的形式很好,没有以强的形式给出,而是以1,2,4,8来对应二进制给出,还要打墙~~~
难点1:怎样判断有没有墙?
位运算可以发挥作用了!
难点2:怎样敲墙?
由于题目中有西是最优的,其次是南,所以枚举的顺序有关系;其次,要先敲N,再去敲E,可以写两个FOR,也可以把它过程化;
小细节:敲的时候两边要属于不同的连通分量啊!
这道题目比以前的代码大大缩减,只有60++,很高兴!#include <stdio.h> #include <string.h> const int max = 50 + 3, dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0}; int num[max][max], map[max][max], s[max*max], qx[max*max], qy[max*max]; int n, m; int newMaxArea = 0, newx, newy, newk; void calc(int j, int k) { for (int i = n; i > 0; i--) // wall second farthest to the south if ( (map[i][j] & (1 << k)) ) { int nowx = i+dx[k], nowy = j+dy[k]; if (num[nowx][nowy] != num[i][j] && s[num[i][j]] + s[ num[i+dx[k]][j+dy[k]] ] > newMaxArea) { newx = i; newy = j; newMaxArea = s[num[i][j]] + s[ num[nowx][nowy] ]; newk = k; } } } int main() { freopen("castle.in", "r", stdin); freopen("castle.out", "w", stdout); scanf("%d%d", &m, &n); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%d", &map[i][j]); /*number the adjusted-parts and get the separate area*/ int number = 0, maxarea = 0; s[0] = - 1 << 20; // let the area outside the border be the minimal area memset(num, 0, sizeof(num)); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (!num[i][j]) { number++; num[i][j] = number; qx[0] = i; qy[0] = j; int first = 0, last = 0; while (first <= last) { for (int k = 0; k < 4; k++) if ( !(map[qx[first]][qy[first]] & (1 << k)) ) { int nowx = qx[first] + dx[k], nowy = qy[first] + dy[k]; if (!num[nowx][nowy]) { qx[++last] = nowx; qy[last] = nowy; num[nowx][nowy] = number; } } first++; } s[number] = last + 1; if (s[number] > maxarea) maxarea = s[number]; } /*get the removed wall*/ for (int j = 1; j <= m; j++) {// wall first farthest to the west calc(j,1); calc(j,2); } /*print the answer*/ printf("%d/n%d/n%d/n%d %d %c/n", number, maxarea, newMaxArea, newx, newy, newk == 1 ? 'N':'E'); return 0; }
小问题:maxarea >?= s[number] 在USACO上编译不能通过?
第二题:Ordered Fractions
题目意思很明白,第一反映排序,而且NLOGN排序可以过,于是没想其他方法,就上了。
看了Analysis后,Russ有一种递归直接生成解的方法,很厉害啊。
We notice that we can start with 0/1 and 1/1 as our ``endpoints'' and recursively generate the middle points by adding numerators and denominators. 0/1 1/1 1/2 1/3 2/3 1/4 2/5 3/5 3/4 1/5 2/7 3/8 3/7 4/7 5/8 5/7 4/5 Each fraction is created from the one up to its right and the one up to its left. This idea lends itself easily to a recursion that we cut off when we go too deep. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> int n; FILE *fout; /* print the fractions of denominator <= n between n1/d1 and n2/d2 */ void genfrac(int n1, int d1, int n2, int d2) { if(d1+d2 > n) /* cut off recursion */ return; genfrac(n1,d1, n1+n2,d1+d2); fprintf(fout, "%d/%d/n", n1+n2, d1+d2); genfrac(n1+n2,d1+d2, n2,d2); } void main(void) { FILE *fin; fin = fopen("frac1.in", "r"); fout = fopen("frac1.out", "w"); assert(fin != NULL && fout != NULL); fscanf(fin, "%d", &n); fprintf(fout, "0/1/n"); genfrac(0,1, 1,1); fprintf(fout, "1/1/n"); }
第三题:Sorting A Three-Valued Sequence
这道题很水,先两个尽量交换,剩下的只能三个交换了,由于最终的排列是给定的,所以只需O(N)的时间
#include <stdio.h> #include <string.h> const int maxn = 1000; int num[4] = {0,0,0,0}, s[4][4]; int a[maxn]; void calc(int start, int end, int sym) { for (int i = start; i <= end; i++) s[sym][a[i]]++; } int min(int a, int b) { if (a > b) return b; else return a; } int main() { freopen("sort3.in", "r", stdin); freopen("sort3.out", "w", stdout); int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); num[a[i]]++; } memset(s,0,sizeof(0)); for (int i = 1; i <= 3; i++) { num[i] = num[i]+num[i-1]; calc(num[i-1]+1,num[i],i); } int tot = 0; for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) if (i != j) { int Min = min(s[i][j], s[j][i]); tot += Min; s[i][j] -= Min; s[j][i] -= Min; } tot += (s[1][2]+s[1][3]) << 1; printf("%d/n", tot); return 0; }
第四题:Healthy Holstein
最多也只有15种饲料,0/1枚举也就32768种,能承受,如果用二进制来构造方案统计的效率比较低,所以还是用DFS了来构造选择方案比较划算
小技巧:记录选择的方案不需要一个数组,只需一个二进制数来记录,方便,快速,给力!
#include <stdio.h> #include <string.h> const int maxneed = 25+3, maxtype = 15+3; int minchoose = 1 << 20, path, choose = 0, minpath, n, type; int map[maxtype][maxneed], get[maxneed], need[maxneed]; bool check() { for (int i = 0; i < n; i++) if (get[i] < need[i]) return false; return true; } void dfs(int now) { if (minchoose < choose) return; //小小的剪枝 if (now == type) { if (choose < minchoose && check()) { minpath = path; minchoose = choose; } return; } //choose path = (path << 1)+1; choose++; for (int i = 0; i < n; i++) get[i] += map[now][i]; dfs(now+1); for (int i = 0; i < n; i++) get[i] -= map[now][i]; choose--; path = path >> 1; //not choose path = path << 1; dfs(now+1); path = path >> 1; } int main() { freopen("holstein.in", "r", stdin); freopen("holstein.out", "w", stdout); scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &need[i]); scanf("%d", &type); for (int i = 0; i < type; i++) for (int j = 0; j < n; j++) scanf("%d", &map[i][j]); path = 0; memset(get,0,sizeof(get)); dfs(0); printf("%d ", minchoose); for (int i = type-1; i >= 0; i--) if (minpath & (1 << i)) printf("%d%c", type-i, --minchoose?' ':'/n'); return 0; }
第五题:Hamming Codes
要用到位运算来获取第N位二进制数的操作;另外可以先预处理出所有数对之间的距离,我想数据本身就不太大,不预处理了,还浪费空间,时间还是挺可以的
#include <stdio.h> #include <string.h> const int max = 256, maxN = 64+3; short int dis[max][max]; int N, B, D; int path[maxN]; bool check(int a, int b) { int dif = 0; for (int i = 0; i < B; i++) if ((a & (1 << i)) != (b & (1 << i))) dif++; if (dif >= D) return true; else return false; } int dfs(int now) { if (now == N) { for (int i = 0; i < N; i++) printf("%d%c", path[i], i % 10 == 9 || i == N-1?'/n':' '); return 1; } int temp = path[now-1]; while (true) { temp++; bool flag = true; for (int i = 0; i < now; i++) if (!check(path[i], temp)) { flag = false; break; } if (flag) { path[now] = temp; if (dfs(now+1)) return 1; } if (temp > (1 << B)-1) { printf("Impossible"); return 1; } } } int main() { freopen("hamming.in", "r", stdin); freopen("hamming.out", "w", stdout); scanf("%d%d%d", &N, &B, &D); memset(path,0,sizeof(path)); path[0] = 0; path[1] = (1 << D) - 1; dfs(2); return 0; }
好久闲着了,今天下午+晚上间歇得把2.1写完了,还是有收获的,明天晚上就要动身去学校了,短暂的寒假结束了,准备迎接新一轮的挑战吧!