• 区间DP


    算法思想

    • 概念:区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来由很大的关系。令状态dp[i][j]表示将下标位置i到j的所有元素合并能获得的价值的最大值.
    • 状态转移方程: (dp[i][j] = max{dp[i][k] + dp[k+1][j] + cost}),cost为将这两组元素合并起来的代价。
    • 特征 :能将问题分解为能两两合并的形式
    • 步骤:由于计算dp[i][j]的值时需要知道所有dp[i][k]和dp[k+1][j]的值,而这两个中包含的元素的数量都小于dp[i][j],所以我们以 len = j-i+1作为DP的阶段。首先从小到大枚举len,然后枚举i的值,根据len和i用公式计算出j的值,然后枚举中间点k,时间复杂度为O(n^3)
    • 核心代码
        MS(dp , 0);//清零
        for(int len=2; len<=n; len++){ //枚举长度
            for(int i=0; i<n; i++){//枚举起点
                int j = i + len - 1;//计算终点
                if(j >= n) break;//不可越界
                if(conditon) state transition...//根据条件写状态转移方程
                for(int k=i; k<j; k++){ //枚举中间点,合并
                    dp[i][j] = max(dp[i][j] , dp[i][k] + dp[k+1][j] + cost)
                }
            }
        }
    

    入门例题

    • 括号最大匹配
    int dp[maxn][maxn];
    string s;
    int n;
    void solve(){
        rep(len , 2 , n){
            rep(i , 0, n - 1){
                int j=i+len-1;
                if(j >= n) break;
                //cout<<s[i]<<": "<<s[j]<<endl;
                if(s[i] == '(' &&  s[j] == ')' ||  s[i] == '[' &&  s[j] == ']') dp[i][j] = dp[i+1][j-1] + 2;
                rep(k, i, j-1){
                    dp[i][j] = max(dp[i][j] , dp[i][k] + dp[k+1][j]);
                }
            }
        }
    }
    
    int main(){
        while(cin>>s){
           
            if(s == "end") break;
            n = s.length();
            MS(dp , 0);
            solve();
            cout<<dp[0][n-1]<<endl;
        }
        return 0;
    }
    
    
    
    • Multiplication Puzzle POJ - 1651
      • 一个序列,头尾不取,每次取掉一个数得分a[i]a[i-1]a[i+1],如何取中间的数字使得最后的得分最少
      • 假设有一个区间[l,r],dp[i][j]表示i,j位置不取,最终可以得到的最小值,对于区间[l,r],最后一步必然是a[l],a[k],a[r]并且取走a[k],k是中间点且不是端点
      • 假设dp[l,k] dp[k][r]已经算出来了,枚举K的位置,状态转移方程(dp[i][j] = min(dp[i][j] , dp[i][k] + dp[k][j] + a[i]*a[k]*a[j]);)
    ll a[maxn];
    ll dp[maxn][maxn];
    
    
    void solve(){
        rep(len , 3 , n){ //枚举区间长度,长度至少是3
            rep(i, 1, n){ //枚举起点
                int j=i+len-1; //得到终点
                if(j > n) break; //检查越界
                dp[i][j] = inf; //求最小值 先初始化inf
                rep(k, i+1, j-1){ //枚举中间点
                    dp[i][j] = min(dp[i][j] , dp[i][k] +  dp[k][j] + a[i]*a[k]*a[j]);
                }
            }
        }
    }
    
    
    int main(){
        while(sc(n) != EOF){
            rep(i,1,n) scl(a[i]); //从1开始读数字
            MS(dp,0);//初始化DP
            solve();
            cout<<dp[1][n]<<endl;
        }
        return 0;
    }
    
    
  • 相关阅读:
    Google Chrome 默认非安全端口列表
    js判断类型的方法
    博客园样式排版自定义
    easyloader.js源代码分析
    JQuery操作cookies
    js获取iframe里面的dom
    封装GetQueryString()方法来获取URL的value值
    js 获取系统时间:年月日 星期 时分秒(动态)
    vue 滚动加载数据
    props 父组件给子组件传递参数
  • 原文地址:https://www.cnblogs.com/czsharecode/p/12308148.html
Copyright © 2020-2023  润新知