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;
    }
    人十我百 人百我万
  • 相关阅读:
    PostgreSQL新手入门
    nodejs获取当前url和url参数值
    nodejs怎么同步从一个数据库查询函数中返回一个值
    linux几种快速清空文件内容的方法
    Redis常用命令(二)
    解读vscode断点调试配置文件【待续】
    以下公司【勿扰】
    思维定律与法则
    运行项目报错183
    css counter的使用方法
  • 原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/10692583.html
Copyright © 2011-2022 走看看