zoukankan      html  css  js  c++  java
  • 区间dp

    区间dp模板题  

    P1880 [NOI1995]石子合并

    先上状态转移方程

    mindp[i][j]=min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]); 
    maxdp[i][j]=max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);  
    

      

    其中d(i,j)意义为从第i堆加到第j堆的个数和

    mindp[i][j]代表第i堆到第j堆的合并最小分数

    maxdp[i][j]代表第i堆到第j堆的合并最大分数

    到了这里 其实这道题就应该迎刃而解了 但是我还是卡了很久

    原因是什么呢

    这道题 是TM环状

    所以我们需要将环拆开 变成一个2n长度的链

    讲到这里 题应该已经结束了 但是还有一个关键点

    就是 我们应该怎么遍历k呢

    先放代码

    #define rep(i,a,b) for(int i = a;i <= b;i++)
        rep(l,2,n){
            rep(i,1,2*n-l+1){
                int j = i+l-1;
                mindp[i][j] = 1000000000;
                maxdp[i][j] = 0;
                rep(k,i,j-1){
                    mindp[i][j] = min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]);
                    maxdp[i][j] = max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);
                 }    
            }    
        }

    在考虑如何递推时,通常考虑如下几个方面:

    是否能覆盖全部状态?

    求解后面状态时是否保证前面状态已经确定?

    是否修改了已经确定的状态?

    也就是说,在考虑递推顺序时,务必参考动态规划的适应对象多具有的性质,具体参考《算法导论》相关或百度百科或wiki。

    既然之前说过我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态。

    如,i=1 to 10,j=1 to 10,k=1 to 9.当i=1,j=5,k=3时,显然状态f[k+1][j]没有结果。

    那么,我们是不是应该考虑枚举k?

    但这样i和j就难以确定了。

    我们不难得到一个两全的方法:枚举j-i,并在j-i中枚举k。这样,就能保证地推的正确。

    ac代码

    #include "cstdio"
    #include "iostream"
    #include "algorithm"
    #include "string.h"
    #define rep(i,a,b) for(int i = a;i <= b;i++)
    using namespace std;
    const int e = 210;
    int n;
    int k;
    int maxdp[e][e],mindp[e][e];
    int psum[e];
    int T[e];
    inline int d(int i,int j){
        return psum[j]-psum[i-1];
    }
    int main(){
        cin >> n;
        rep(i,1,n){
            cin >> T[i];
            T[i+n]=T[i];
        }
        rep(i,1,2*n){
            psum[i]=psum[i-1]+T[i];
        }
        rep(l,2,n){
            rep(i,1,2*n-l+1){
                int j = i+l-1;
                mindp[i][j] = 1000000000;
                maxdp[i][j] = 0;
                rep(k,i,j-1){
                    mindp[i][j] = min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]);
                    maxdp[i][j] = max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);
                 }    
            }    
        }
        int minn = 1000000000,maxx = 0;
        rep(i,1,n){
            minn = min(minn,mindp[i][i+n-1]);
            maxx = max(maxx,maxdp[i][i+n-1]);
        }
        printf("%d
    %d",minn,maxx);
        return 0;
    }
    人十我百 人百我万
  • 相关阅读:
    洛谷 P1226 【模板】快速幂||取余运算 题解
    洛谷 P2678 跳石头 题解
    洛谷 P2615 神奇的幻方 题解
    洛谷 P1083 借教室 题解
    洛谷 P1076 寻宝 题解
    洛谷 UVA10298 Power Strings 题解
    洛谷 P3375 【模板】KMP字符串匹配 题解
    Kafka Shell基本命令
    Mybatis与Hibernate的详细对比
    MyBatis简介
  • 原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/10692583.html
Copyright © 2011-2022 走看看