DP

  • 动态规划 Lv1

P1156 垃圾陷阱

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     }
View Code

 

小a和uim之大逃离

看到数据范围

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);
View Code

 

P2467 [SDOI2010]地精部落

能力有限 右转洛谷

 

拆分数列

正向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 }
cmp
 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     }
框架

 

[SDOI2009]学校食堂

这道题。。。想暴力都难。。。

而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\n", ans);
View Code

 

posted @ 2018-06-21 21:44  hjmmm  阅读(137)  评论(0编辑  收藏  举报