• 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
    ", ans);
    View Code
  • 相关阅读:
    MySQL高级查询总结
    MySQL数据库作业
    MySQLdump备份还原命令
    MySQL之Join
    MySQL课堂作业(一)
    Mysql数据库
    Js实例之简易计算器
    JS系统函数
    js课堂作业之转换月份
    C++ Name Mangling 为什么不编码返回值参数
  • 原文地址:https://www.cnblogs.com/hjmmm/p/9211080.html
Copyright © 2020-2023  润新知