- 动态规划 Lv1
f [ hight ] = life;
一开始想的是用life做下标 看来还是要多方面想想啊…
这是个变种背包【只是蒟蒻这么觉得…
取不取 取哪个的问题蒟蒻觉得都能跟背包搭上边
最初想法:
如果堆 那么生命就是能到达它的点的最大生命
如果吃 那就把能到达它的点生命都续命
【然鹅蒟蒻一开始做的时候并没有背包的觉悟 以为是个线性的卡了半天…
这样做仿佛可以?
但会导致冗余操作 即堆垃圾的操作会导致不完全跳跃
我们的目标仅仅是出坑 所以不需要这种操作
【呵呵呵cnblogs又吞我稿
于是优化后的算法如下
首先把垃圾按出现时间排序
然后对于每个垃圾i
枚举所有高度j
如果f[j] > i的出现时间
说明当前高度还来得及做垃圾i的操作
状态转移如下
堆 : f[j] = max(f[j], f[j + height[i]]);
吃 : f[j] += eat[i];
正如背包 若要压一维 则j倒序
另外 随时判定能否出坑 能则直接跳出
最后出不了坑的话 答案就是f[0] 相当于所有垃圾都用来吃
1 f[0] = 10; 2 for(int i = 1; i <= m; i++) 3 for(int j = n; j >= 0; j--) 4 if(f[j] >= node[i].t){ 5 if(j + node[i].h >= n){ 6 printf("%d", node[i].t); 7 return 0; 8 } 9 f[j + node[i].h] = max(f[j + node[i].h], f[j]); 10 f[j] += node[i].w; 11 } 12 printf("%d", f[0]);
由于影响最后答案的是取数顺序
所以这是一道典型区间dp
记得高精哈
1 for(int i = 1; i <= m; i++){ 2 scanf("%s", a[i]); 3 mul(a[i], bin[m], f[i][i]); 4 } 5 for(int i = 1; i < m; i++){ 6 for(int j = 1; j <= m - i; j++){ 7 char tmp[2][1000]; 8 mul(bin[m - i], a[j], tmp[0]); 9 add(f[j + 1][j + i], tmp[0], tmp[0]); 10 mul(bin[m - i], a[j + i], tmp[1]); 11 add(f[j][j + i - 1], tmp[1], tmp[1]); 12 give(cmp(tmp[0], tmp[1]) ? tmp[0] : tmp[1] , f[j][j + i]); 13 } 14 } 15 add(ans, f[1][m], ans); 16 }
看到数据范围
n,m<=800,1<=k<=15
大概是个n方级,有关n,m,k的状态转移
先列它三维f[n][m][k]
在当前格子的不是小a就是uim
所以f[x][y][z][1 / 0]表示 0 :小a 1:uim在当前格(x, y)取z的方案数
那么每个格子一开始就是f[x][y][map[x][y]][0] = 1;
最后求f[x][y][0][1]的和
1 for(int i = 1; i <= n; i++) 2 for(int j = 1; j <= m; j++){ 3 scanf("%d", &map[i][j]); 4 f[i][j][map[i][j] % p][0] = 1; 5 } 6 //初始化 7 for(int i = 1; i <= n; i++) 8 for(int j = 1; j <= m; j++) 9 for(int q = 0; q < p; q++){ 10 f[i][j][q][0] = (f[i][j][q][0] + f[i - 1][j][(q - map[i][j] + p) % p][1]) % P; 11 f[i][j][q][0] = (f[i][j][q][0] + f[i][j - 1][(q - map[i][j] + p) % p][1]) % P; 12 f[i][j][q][1] = (f[i][j][q][1] + f[i - 1][j][(q + map[i][j] + p) % p][0]) % P; 13 f[i][j][q][1] = (f[i][j][q][1] + f[i][j - 1][(q + map[i][j] + p) % p][0]) % P; 14 } 15 //状态转移 16 long long ans = 0; 17 for(int i = 1; i <= n; i++) 18 for(int j = 1; j <= m; j++){ 19 ans = (ans + (long long)f[i][j][0][1]) % P; 20 } 21 //统计 22 printf("%lld", ans);
能力有限 右转洛谷
正向dp求最后一个数的最小值
逆向dp求第一个数最大值
注意零的处理
1 bool cmp(int f1, int t1, int f2, int t2){ 2 while(!a[f1] && f1 < t1) f1++; 3 while(!a[f2] && f2 < t2) f2++; 4 int len1 = t1 - f1 + 1, len2 = t2 - f2 + 1; 5 if(len1 != len2 ) return len1 > len2; 6 for(int i = 0; i < len1; i++) 7 if(a[f1 + i] != a[f2 + i]) return a[f1 + i] > a[f2 + i]; 8 return 0; 9 }
1 scanf("%s", str); 2 n = strlen(str); 3 for(int i = 1; i <= n; i++) a[i] = str[i - 1] - '0'; 4 a[0] = -1; 5 f1[1] = 1; 6 for(int i = 2; i <= n; i++){ 7 for(int j = i; j >= 1; j--){ 8 if(cmp(j, i, f1[j - 1], j - 1)){ 9 f1[i] = j; break; 10 } 11 } 12 } 13 while(!a[f1[n] - 1]) f1[n]--; 14 //for(int i = 1; i <= n; i++) printf("%d ", f1[i]); 15 f2[f1[n]] = n; 16 for(int i = f1[n] - 1; i >= 1; i--){ 17 for(int j = f1[n]; j >= i; j--){ 18 if(cmp(j + 1, f2[j + 1], i, j)){ 19 f2[i] = j; break; 20 } 21 } 22 } 23 int i = 1; 24 while(i <= n){ 25 for(int j = i; j <= f2[i]; j++) printf("%d", a[j]); 26 i = f2[i] + 1; 27 if(i <= n) printf(","); 28 }
这道题。。。想暴力都难。。。
而dp就是让我们的枚举优雅一些
如果要写个暴力的话
当一个人自己被选的时候走到下一个数 就可以保证到最后的时候所有人都打完
或者也可以选择在容忍范围内选别的人
然后要判断各个未被选数的最低容忍范围在哪里
然后分分钟T爆
。。。
那怎么办呢? 当然要优雅
1 ≤ N ≤ 1,000,0 ≤ Ti ≤ 1,000,0 ≤ Bi ≤ 7,1 ≤ C ≤ 5。
显然我们要做一个与bi有关的dp
我们在上述暴力的时候需要什么?
自己的信息 okk
容忍范围 。。。 最大是7嘛
所以状压 维护后7位的状态就好了
在当前状态中 由于要统计时间
一样的状态 结束者不同也会导致答案不同
所以再开一维
至于dp维护的值 当然是最短时间啦……
这道题用填表还是刷表呢?显然后者
1 while(T--){ 2 scanf("%d", &n); 3 for(int i = 1; i <= n; i++){ 4 scanf("%d%d", &a[i], &end[i]); 5 end[i] += i; 6 } 7 memset(f, inf, sizeof(f)); 8 f[1][0][7] = 0; 9 for(int i = 1; i <= n; i++) 10 for(int j = 0; j < (1 << 8); j++)//枚举状态 11 for(int k = -8; k <= 7; k++) 12 if(f[i][j][k + 8] != inf){ 13 if(j & 1) 14 f[i + 1][j >> 1][k + 7] = min(f[i + 1][j >> 1][k + 7], f[i][j][k + 8]); 15 else{ 16 lir = inf; 17 for(int h = 0; h <= 7; h++) 18 if(!((j >> h) & 1)){ 19 if(i + h > lir) break; 20 lir = min(lir, end[i + h]);//维护容忍范围 21 f[i][j | (1 << h)][h + 8] = min(f[i][j | (1 << h)][h + 8], 22 f[i][j][k + 8] + (i + k ? (a[i + k] ^ a[i + h]) : 0));//i+h要打饭 23 } 24 } 25 } 26 int ans = inf; 27 for(int i = 0; i <= 8; i++) 28 ans = min(f[n + 1][0][i], ans); 29 printf("%d ", ans);