zoukankan      html  css  js  c++  java
  • 区间dp+四边形不等式优化

    区间dp+四边形优化

    luogu:p2858

    题意

    给出一列数 (v_i),每天只能取两端的数,第 j 天取数价值为(v_i imes j),最大价值??

    转移方程

    dp[i][j] :n天卖掉i..j货物的收益

    dp[begin][end]=max(dp[begin][end-1]+value[end]*(n-len+1) ,dp[begin+1][end]+value[begin]*(n-len+1));
    

    注意理解

    代码

    递推形式

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define mm1(x) memset(x,-1,sizeof(x))
    #define maxn 2010
    int dp[maxn][maxn],value[maxn];
    int n;
    int solve(){
        for(int i=1;i<=n;i++){
            dp[i][i]=value[i]*n;
            //*key:
        }
        //枚举长度:
        for(int len=2;len<=n;len++){
            //枚举起点
            for(int begin=1;begin<=n-len+1;begin++){
                int end=begin+len-1;
                dp[begin][end]=max(dp[begin][end-1]+value[end]*(n-len+1)
                ,dp[begin+1][end]+value[begin]*(n-len+1));
    
            }
    
        }
        return dp[1][n];
    
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",value+i);
        }
        mm1(dp);
        printf("%d
    ",solve());
        return 0;
    }
    

    记忆化搜索

    #include<bits/stdc++.h>
    using namespace std;
    //记忆化搜素
    #define maxn 2010
    int dp[maxn][maxn],value[maxn];
    #define mm(x) memset(x,-1,sizeof(x));
    int dfs(int i,int j,int num){
        if(i>j) return 0;
        if(dp[i][j]!=-1) return dp[i][j];
        else{
            dp[i][j]=max(value[i]*num+dfs(i+1,j,num+1),
            value[j]*num+dfs(i,j-1,num+1));
        }
        return dp[i][j];
    }
    int n;
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",value+i);
        }
        mm(dp);
        int ans=dfs(1,n,1);
        printf("%d
    ",ans);
        return 0;
    }
    

    记忆化搜索很好理解也方便些,是追求解题速度的很好选择

    p1880

    题意

    石子合并问题:(环形,最小值+最大值)

    题解

    环形,可用(2n)长度,将元素复制一份

    转移方程

    dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+sum[j]-sum[i-1]);
    对于最小值可用四边形不等式优化
    dpmax[i][j]=min(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+sum[j]-sum[i-1]);
    对于最大值,某大佬题解中提到可优化之讨论端点情况(但本渣没有弄清楚)
    dpmax[i][j]=max(dpmax[i][j-1]+sum[j]-sum[i-1],dpmax[i+1][j]+sum[j]-sum[i-1]);

    四边形不等式优化

    四边形不等式优化核心满足条件:
    记决策点为(k=s[i][j])
    如果(s[i][j-1]<=k<=s[i+1][j]),则枚举k时,只需从s[i][j-1]枚举到s[i+1][j]$(因为这两者区间长度较短,已经被求出)
    下面是重要的定理(不加证明的使用):
    对于dp[i][j]=min(dp[i][k]+dp[k+1][j]+cost[i][j])

    区间包含的单调性:如果小区间包含于大区间中,那么小区间的cost值不超过大区间的cost值
    四边形不等式:两个交错区间的cost的和不超过小区间与大区间的cost的和

    满足上述性质的cost,能够推出dp[i][j]满足四边形不等式,s[i][j]=k也满足上述性质。
    综上,能够优化的关键在于cost[i][j]满足上述两个性质。*

    代码

    未优化代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 205
    int dp1[maxn][maxn],dp2[maxn][maxn],value[maxn];
    int sum[maxn];
    //value[i]=value[i+n]
    //区间dp
    //dp[i][j]表示i..j最优得分
    //O(N^3)
    int n;
    int min_ans=0x3f3f3f3f,max_ans=-1;
    #define mm1(x) memset(x,-1,sizeof(x));
    #define mm2(x) memset(x,0x3f,sizeof(x));
    void init(){
        mm1(dp1);
        mm2(dp2);
        for(int i=1;i<=2*n;i++){
            sum[i]=sum[i-1]+value[i];
        }
    }
    void solve(){
        for(int i=1;i<=2*n;i++){
            dp1[i][i]=dp2[i][i]=0;
    
        }
        for(int len=2;len<=n;len++){
            for(int begin=1;begin<=(2*n-len+1);begin++){
                int end=begin+len-1;
                for(int j=begin;j<=end-1;j++){
                    dp1[begin][end]=max(dp1[begin][end],dp1[begin][j]+dp1[j+1][end]+sum[end]-sum[begin-1]);
                    dp2[begin][end]=min(dp2[begin][end],dp2[begin][j]+dp2[j+1][end]+sum[end]-sum[begin-1]);
                }
            }
        }
        for(int i=1;i<=n;i++){
            //printf("db max:%d min:%d
    ",dp1[i][i+n-1],dp2[i][i+n-1]);
            max_ans=max(max_ans,dp1[i][i+n-1]);
            min_ans=min(min_ans,dp2[i][i+n-1]);
        }
    
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",value+i);
            value[i+n]=value[i];
        }
        init();
        solve();
        printf("%d
    %d
    ",min_ans,max_ans);
        return 0;
    
    }
    

    四边形不等式优化代码:

    #include<bits/stdc++.h>
    using namespace std;
    //区间dp上的四边形优化
    #define inf 0x3f3f3f3f
    #define maxn 210
    int sum[maxn],value[maxn];
    int dpmax[maxn][maxn],dpmin[maxn][maxn];
    int s[maxn][maxn];// min最优决策点
    int n;
    #define mm0(x) memset(x,0x3f,sizeof(x))
    #define mm1(x) memset(x,-1,sizeof(x))
    void init(){
        mm1(dpmax);
        mm0(dpmin);
        for(int i=1;i<=2*n;i++){
            sum[i]=sum[i-1]+value[i];
            //printf("db i:%d sum[i] %d
    ",i,sum[i]);
        }
    
    }
    int minv=inf,maxv=0;
    void solve(){
        for(int i=1;i<=2*n;i++){
            dpmax[i][i]=dpmin[i][i]=0;
            s[i][i]=i;
        }
        
        for(int len=2;len<=n;len++){
            for(int i=1;i+len-1<=2*n;i++){
                int j=i+len-1;
                dpmax[i][j]=max(dpmax[i][j-1]+sum[j]-sum[i-1],dpmax[i+1][j]+sum[j]-sum[i-1]);
                //某大佬认为最大值取得必然最后一次合并在左右两端
                //目前自己没有想通和证明
                int idx;
                for(int k=s[i][j-1];k<=s[i+1][j];k++){
                    if((dpmin[i][k]+dpmin[k+1][j]+sum[j]-sum[i-1])<dpmin[i][j]){
                        dpmin[i][j]=dpmin[i][k]+dpmin[k+1][j]+sum[j]-sum[i-1];
                        idx=k;
                    }
                    s[i][j]=idx;               
                }
               // printf("db min: %d k:%d i:%d j: %d
    ",dpmin[i][j],s[i][j],i,j);
            }
        }
        for(int i=1;i<=n;i++){
            //printf("db i:%d %d
    ",i,dpmin[i][i+n-1]);
            minv=min(dpmin[i][i+n-1],minv);
            maxv=max(dpmax[i][i+n-1],maxv);
        }
        
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",value+i);
            value[i+n]=value[i];
        }
        init();
        solve();
        printf("%d
    %d
    ",minv,maxv);
        return 0;
    }
    
  • 相关阅读:
    es5中,一个在js中导入另一个js文件。
    移动端字体小于12号字的时候,line-height居中的问题
    初学者都能懂得 Git 说明
    一探 Vue 数据响应式原理
    文件的命名规则
    Vue 的 watch 和 computed 有什么关系和区别?
    MVC 与 Vue
    博客园皮肤设置
    java使用run和start后的线程引用
    Python改变一行代码实现二叉树前序、中序、后序的迭代遍历
  • 原文地址:https://www.cnblogs.com/fridayfang/p/10351303.html
Copyright © 2011-2022 走看看