区间dp+四边形不等式优化

区间dp+四边形优化

luogu:p2858

题意

给出一列数 \(v_i\),每天只能取两端的数,第 j 天取数价值为\(v_i \times 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\n",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\n",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\n",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\n%d\n",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\n",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\n",dpmin[i][j],s[i][j],i,j);
        }
    }
    for(int i=1;i<=n;i++){
        //printf("db i:%d %d\n",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\n%d\n",minv,maxv);
    return 0;
}

原文地址:https://www.cnblogs.com/fridayfang/p/10351303.html

时间: 2024-08-12 10:35:20

区间dp+四边形不等式优化的相关文章

hdu 3506 Monkey Party 区间dp + 四边形不等式优化

http://acm.hdu.edu.cn/showproblem.php?pid=3506 四边行不等式:http://baike.baidu.com/link?url=lHOFq_58V-Qpz_nTDz7pP9xCeHnd062vNwVT830z4_aQoZxsCcRtac6CLzbPYLNImi5QAjF2k9ydjqdFf7wlh29GJffeyG8rUh-Y1c3xWRi0AKFNKSrtj3ZY7mtdp9n5W7M6BBjoINA-DdplWWEPSK#1 dp[i][j]表示第

hdu 3480 dp 四边形不等式优化

http://acm.hdu.edu.cn/showproblem.php?pid=3480 给出一个数字集合 S,大小为 n,要求把这个集合分成m个子集,每分出一个子集的费用是子集中的 (max-min)^2,求最小费用. 开始的dp转移很容易想到. 首先对集合从小到大排序,dp[i][j] 表示前i个元素被分成j个子集的最小费用.然后枚举最后一个子集. dp[i][j] = min{dp[k-1][j-1] + cost(k, i)}; 这个转移明显是过不去的,n<10000 m<5000

[51nod 1022] 石子归并v2 [dp+四边形不等式优化]

题面: 传送门 思路: 加强版的石子归并,现在朴素的区间dp无法解决问题了 首先我们破环成链,复制一条一样的链并粘贴到原来的链后面,变成一个2n长度的序列,在它上面dp,效率O(8n^3) 显然是过不了的,需要优化 注意:dp的转移如下:dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum(i,j)),其中sum(i,j)表示i到j的价值和,满足区间单调性 因此dp[i][j]也满足区间单调性,可以用四边形不等式优化 我们令s[i][j]等于让dp[i][j]取最小值的那个K

HDU 3516 DP 四边形不等式优化 Tree Construction

设d(i, j)为连通第i个点到第j个点的树的最小长度,则有状态转移方程: d(i, j) = min{ d(i, k) + d(k + 1, j) + p[k].y - p[j].y + p[k+1].x - p[i].x } 然后用四边形不等式优化之.. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <

HDU-2829 Lawrence (DP+四边形不等式优化)

题目大意:有n个敌方军火库呈直线排列,每个军火库有一个值vi,并且任意相邻的两个库之间都有通道相连.对于任意一条连起来的军火库链,它对我方的威胁可以用函数w(i,j)表示为:w(i,j)=vi*sum(i+1,j)+w(i+1,j)   i<j; w(i,j)=0               i=j; 现在,你有m个炸弹,每颗可以炸掉相邻的两个库之间的通道,求最终的总的最小威胁值. 题目分析:定义状态dp(i,j)表示用 i 颗炸弹使前 j 个库房脱离链条后前 j 个库房产生的最小威胁值,则状态

hdu 2829 dp+四边形不等式优化

用w[i][j]表示i到j之间没有边毁掉的费用. 有一种很好证明w[i][j]是否满足四边形不等式的条件. 若(w[i+1][j]-w[i][j])是关于j的减函数,就是满足条件的.可以证明这里的w[i][j]是瞒住条件的. #include <set> #include <map> #include <queue> #include <stack> #include <cmath> #include <string> #includ

HDU 3506 DP 四边形不等式优化 Monkey Party

环形石子合并问题. 有一种方法是取模,而如果空间允许的话(或者滚动数组),可以把长度为n个换拓展成长为2n-1的直线. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 8 const int maxn = 2000 + 10; 9 const int INF = 0x3f3f3f3f

四边形不等式优化DP——石子合并问题 学习笔记

好方啊马上就要区域赛了连DP都不会QAQ 毛子青<动态规划算法的优化技巧>论文里面提到了一类问题:石子合并. n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分. 求出将n堆石子合并成一堆的最小得分和最大得分以及相应的合并方案. 设m[i,j]表示合并d[i..j]所得到的最小得分. 状态转移方程: 总的时间复杂度为O(n3). [优化方案] 四边形不等式: m[i,j]满足四边形不等式 令s[i,j]=max{k | m[

石子合并(四边形不等式优化dp)

该来的总是要来的———————— 经典问题,石子合并. 对于 f[i][j]= min{f[i][k]+f[k+1][j]+w[i][j]} From 黑书 凸四边形不等式:w[a][c]+w[b][d]<=w[b][c]+w[a][d](a<b<c<d) 区间包含关系单调: w[b][c]<=w[a][d](a<b<c<d) 定理1:  如果w同时满足四边形不等式和决策单调性 ,则f也满足四边形不等式 定理2:  若f满足四边形不等式,则决策s满足 s[i