http://poj.org/problem?id=3605
这个暑假的个人赛打的一塌糊涂...成绩有多难看还真不想说...
这么多场个人赛,暴露出我的知识面是多么狭窄..可能也是跟我考试前荒废了那么一段时间是有关的..
记录比赛中一道无聊的,超简单的,可是我那时做不出的一道树dp中最水的题。题意很简单的,不解释太多,就简单说一下我的做法。一棵与或表达式树,操作符都已经排好序了,于是我从最底层开始,逐层往上dp。dp数组是储存该节点从0变成1最少要改变多少个控制信号,
在每一层里,如果是与:dp[i] = dp[i << 1] + dp[i << 1 | 1];
如果是或:dp[i] = min2(dp[i << 1] + dp[i << 1 | 1], min2(dp[i << 1 | 1], dp[i << 1]));
有一个很关键的地方是,除了第一次从0变1要改变dp[0]次,其他任何一次无论是从0变成1还是从1变成0,都只需改变一个控制信号。其实原因很简单,既然能找到一个从初始状态(全部是0)改变dp[0]次就可以把输出信号变成1的方法,那么如果只改变dp[0]-1次,这时的输出必然是0。也就是说,除了第一次从0变成1是要找最小操作,其余都只需1步的操作就可以完成了。
View Code
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 5 #define debug 0 6 7 int min2(int _a, int _b){return _a < _b ? _a : _b;} 8 int max2(int _a, int _b){return _a > _b ? _a : _b;} 9 10 int dp[8200]; 11 int op[8200]; 12 char s[10005]; 13 14 void init(int k){ 15 int end = 1 << k; 16 17 for (int i = 0; i < end; i++){ 18 dp[i] = 1; 19 } 20 } 21 22 int deal(int k){ 23 if (!k) return dp[0]; 24 int len = 1 << (k - 1); 25 26 for (int i = 0; i < len; i++){ 27 int p = i + len; 28 29 if (op[p]){ 30 dp[i] = dp[i << 1] + dp[i << 1 | 1]; 31 } 32 else{ 33 dp[i] = min2(dp[i << 1] + dp[i << 1 | 1], min2(dp[i << 1 | 1], dp[i << 1])); 34 } 35 } 36 #if debug 37 for (int i = 0; i < len << 1; i++){ 38 printf("%d\n", dp[i]); 39 } 40 puts(""); 41 #endif 42 43 return deal(k - 1); 44 } 45 46 int cnt(int n){ 47 int k = -1; 48 49 while (n){ 50 k++; 51 n /= 2; 52 } 53 54 return k; 55 } 56 57 int main(){ 58 int c; 59 60 scanf("%d", &c); 61 62 while (c--){ 63 int n; 64 65 scanf("%d", &n); 66 scanf("%s", s); 67 for (int i = 1; i < n; i++){ 68 scanf("%d", &op[i]); 69 } 70 71 init(cnt(n)); 72 int tmp = deal(cnt(n)); 73 #if debug 74 printf("cnt %d tmp %d\n", cnt(n), tmp); 75 #endif 76 77 int len = strlen(s); 78 int p = 0, cnt = 0; 79 80 while (p < len && s[p] == '0') p++; 81 if (p != len){ 82 cnt += tmp; 83 } 84 for (int i = p + 1; i < len; i++){ 85 if (s[i] != s[i - 1]) cnt++; 86 } 87 printf("%d\n", cnt); 88 } 89 90 return 0; 91 }