USACO 2.2 分析

 题目一:Preface Numbering

题目大意:求1——N之间的十进制数用罗马数字表示时给个字母要用几次(IVXLCDM)

算法:枚举

基本思想是枚举每一个数字分别转换成罗马形式统计再统计,但这种方法很多东西重复了,更简单的方法是统计各个数字在各位上的出现情况,然后做一张表。因为不同数字在不同位的情况很少只有9*Length种,具体实现的细节在代码中有解释

 /* ID: zhangji42 TASK: subset LANG: C++ */ #include <stdio.h> #include <string.h> #define MAXV 781 int s[MAXV]; int main() { freopen("subset.in", "r", stdin); freopen("subset.out", "w", stdout); int n; scanf("%d", &n); memset(s, 0, sizeof(s)); s[0] = 1; int v = 0, m; for (int i = 1; i < n; i++) { v += i; for (int j = v; j >= i; j--) s[j] += s[j-i]; } v += n; if (v & 1 == 1) printf("0/n"); else { v = v >> 1; printf("%d/n", s[v-n]); } return 0; }

题目二:Subset Sums

题目大意:把1——N分成两组,每组的和要相等,这样的分法有多少种

算法:Dynamic Programming--背包

容易想到和背包有很多类似的地方,所以可以借鉴背包的思想,s[i]表示当前无论以哪个数字结尾的和为i的排列的种数,难点在于不重复。

但是也容易发现,只要把最后的N固定住就可以了;或者是最终的答案分为两类,以N结尾的刚好一半,以下是前一种解法

/* ID: zhangji42 TASK: preface LANG: C++ */ #include <stdio.h> #include <string.h> int degree; char *ch = "IVXLCDM"; int times[4][10];//record different digit appear in different place; int tot[8]; /* 0 I 1 1 V 5 2 X 10 3 L 50 4 C 100 5 D 500 6 M 1000 */ int main() { freopen("preface.in", "r", stdin); freopen("preface.out", "w", stdout); int n; scanf("%d", &n); memset(times, 0, sizeof(times)); for (int i = 1; i <= n; i++) { int temp = i; degree = 0; while (temp > 0) { times[degree][temp % 10]++; temp /= 10; degree++; } } memset(tot, 0, sizeof(tot)); for (int i = 0; i < degree; i++) for (int j = 1; j <= 9; j++) if (!times[i][j]) { i = degree; break; } else { int temp = times[i][j]; switch (j) { case 0: break; case 1:case 2:case 3: tot[i << 1] += j * temp; break; case 4:case 5: tot[i << 1] += (5-j) * temp; tot[(i << 1)+1] += temp; break; case 6:case 7:case 8: tot[(i << 1)+1] += temp; tot[i << 1] += (j-5)*temp; break; case 9: tot[i*2] += temp; tot[(i+1) << 1] += temp; break; } } for (int i = 0; i < 8; i++) { if (!tot[i]) break; printf("%c %d/n", ch[i], tot[i]); } return 0; }

 

题目三:Runround Numbers

题目大意:给定一个数,找到最小大于此数的符合规则的数。

算法:枚举

依次增大判断的数,先去判断数字有否重复,再去判断是否符合规则,由于次数给定,所以能够一次性确定下一个判断的数位

/* ID: zhangji42 TASK: runround LANG: C++ */ #include <stdio.h> #include <string.h> bool h[10]; int a[10]; int len, n;//len for recording the length of the number bool checknum(int num) { memset(h,0,sizeof(h)); //in this place h[i] means the appearance of number i memset(a,0,sizeof(a));// record the number int temp = num; len = 0; h[0] = 1;//zero can't appear while (temp > 0) { a[len] = temp % 10; if (h[a[len]]) return false; h[a[len]] = 1; temp /= 10; len++; } for (int i = 0; i < (len+1)/2; i++) { //reverse the number int temp = a[i]; a[i] = a[len-i-1]; a[len-i-1] = temp; } return true; } bool check(int num) { if (!checknum(num)) return false; int pos = a[0] % len, res = len; memset(h,0,sizeof(h)); // h[i] has a another meaning, which means whether position i is visited while (!h[pos]) { h[pos] = 1; res--; pos = (pos+a[pos]) % len; } //notice that we have to return to the first place to get a circle return (!res); // if every digit is visited then res = 0 (right) else res > 0(wrong) } int main() { freopen("runround.in", "r", stdin); freopen("runround.out", "w", stdout); scanf("%d", &n); while (true) { if (check(++n)) { printf("%d/n", n); break; }; } return 0; }

 

题目四:Party Lamps

题目大意:开始时N盏灯都是亮着的,有四种操作可以转变灯的亮暗情况,给定N,C(操作的总次数),以及最后某些灯的亮暗情况,求出所有符合的最终的灯的情况

算法:DFS

注意到一个操作两次等于不操作,所以四种操作最终产生2^4 = 16种情况,至于操作次数,只要有用的不大于实际的,并且相差2的倍数就行,最后把得到的字符串排序

 

优化:还有一个很大的优化就是重复性,每6个一个重复,发现了这个,尽管大胆干吧。

/* ID: zhangji42 TASK: lamps LANG: C++ */ #include <iostream> #include <string.h> #include <string> #include <stdio.h> using namespace std; const int maxn = 10000+2; int N, C, tot_on, on[maxn], tot_off, off[maxn]; int lamp[maxn], change, tot, c[4][maxn]; string s[16], temp; void build() { memset(c,0,sizeof(c)); for (int i = 0; i < N; i++) c[0][i] = 1; for (int i = 0; i < N; i = i+2) c[1][i] = 1; for (int i = 1; i < N; i = i+2) c[2][i] = 1; for (int i = 0; i < N; i = i+3) c[3][i] = 1; } bool check() { for (int i = 0; i < tot_on; i++) if (!lamp[on[i]-1]) return false; for (int i = 0; i < tot_off; i++) if (lamp[off[i]-1]) return false; return true; } void dfs(int step) { if (step == 4) { if (check() && change <= C && (C-change) % 2 == 0) { s[tot] = ""; for (int i = 0; i < N; i++) { char c = lamp[i]+'0'; s[tot] = s[tot] + c; } s[tot][N] = '/0'; tot++; } return; } dfs(step+1); change++; for (int i = 0; i < N; i++) lamp[i] ^= c[step][i]; dfs(step+1); for (int i = 0; i < N; i++) lamp[i] ^= c[step][i]; change--; } int main() { freopen("lamps.in", "r", stdin); freopen("lamps.out", "w", stdout); scanf("%d%d", &N, &C); int n; tot_on = 0; while (true) { scanf("%d", &n); if (n == -1) break; on[tot_on++] = n; } tot_off = 0; while (true) { scanf("%d", &n); if (n == -1) break; off[tot_off++] = n; } /*start to dfs the state of the switches*/ for (int i = 0; i < N; i++) lamp[i] = 1; change = 0; build(); tot = 0; dfs(0); /*sort and print the string*/ for (int i = 0; i <tot-1; i++) for (int j = i+1; j < tot; j++) if (s[i] > s[j]) { temp = s[i]; s[i] = s[j]; s[j] = temp; } if (!tot) printf("IMPOSSIBLE/n"); else for (int i = 0; i < tot; i++) if (!i || s[i] != s[i-1]) cout << s[i] << endl; return 0; }

你可能感兴趣的:(USACO 2.2 分析)