洛谷P1880 石子合并
作者:互联网
此题是基于单纯线性的石子合并(单纯的石子合并https://vjudge.net/problem/51Nod-1021)基础上进行了修改,增加了环形这一条件。
我们先忽略掉环形首先从单纯线性合并做起,我们写出这样一个DP方程式dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+d(i,j)),
dp[i][j]表示从i合并到j的最小值,我们分别求得每个区间的最小,最后进行合并,最终的最大值一定是最小的,容易反证证明。
那么我们以k作为一个分界线例如,我要合并1~5的数字,那么我就有1~1 & 2~5 1~2 & 3~5 1~3 & 4~5 1~4 & 5~5 这几种选择再加上1~5的前缀和,那么我们就可以解决这一问题,接下来我们考虑如何去解决环形这一问题 我们注意到例如1 2 3 4可以变为 2 3 4 1 、3 4 1 2 、4 1 2 3 于是我们可以将这个数字串扩为2倍,并不断去算不同排列下的最优解,这样我们就解决了这一问题。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int a[201]; int dpmax[202][202],d[201],dpmin[202][202]; int cal(int i,int j) {return d[j] - d[i-1];} int main() { int n; cin>>n; for(int i=1; i<=n; i++) { cin>>a[i]; d[i] = d[i-1] + a[i]; a[i+n] = a[i]; } for(int i=n+1; i<=n*2; i++) { d[i] = d[i-1] + a[i]; } for(int len=1; len<=n-1; len++) { for(int i=1,j=i+len; (i<n*2)&&(j<n*2); i++,j=i+len) { dpmin[i][j] = 99999999; for(int k=i; k<j; k++) { dpmax[i][j] = max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+cal(i,j)); dpmin[i][j] = min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+cal(i,j)); } } } int ans1=-1,ans2=99999999; for(int i=1; i<=n; i++){ ans1 = max(ans1,dpmax[i][i+n-1]); ans2 = min(ans2,dpmin[i][i+n-1]); } cout<<ans2<<endl<<ans1; return 0; }总体观察下来,我们发现区间DP有点像分治
但是此题的复杂度为O(n^3) 因此我们采用平行四边形不等式进行优化,使最终复杂度为O(n^2);
首先我们给出如下定义:
1.如果函数w满足: w[a,c] + w[b,c] \leq w[b,c]+w[a,d](a<b<c<d) 则w满足四边形不等式(简称w为凸)2.如果函数w满足: w[i,j]<=w[i'][j'] ([i,j]包含于[i',j']) 则说w关于区间包含关系单调 四边形不等式优化:1.对于一个函数w,如果a<=b<=c<=d,函数w有w(a,c)+w(c,d)<=w(a,d)+w(b,c),即交叉小于包含,我们就说函数w满足四边形不等式 2.对于另一个函数m,如果有m(i,j)=min{m(i,k−1),m(k,j)}+w(i,j)(i≤k≤j),且w具有区间包含单调性我们就说m也满足四边形不等式, 3.假如m满足四边形不等式,那么s(i,j)单调 即s(i,j)≤s(i,j+1)≤s(i+1,j+1),s是记录m函数最优时下标的位置。 我们在此只是利用其中的结论。注意,同时注意这里的m取得必须是min,对于max并不满足上述内容,但对于max又有另一个结论,我们直接给出 即 使最大值最优的决策P[i][j]要么是i,要么是j−1 因此我们可以使用这一优化,让每次的分界线决策在一个常数内,因此整个算法的复杂度为O(n^2); 第一重循环控制长度,第二重循环控制DP位置,第三重循环控制每次循环时的分界线->第三重循环控制从s[i][j-1]~s[i+1][j]选择分界线 既然我们进行了优化,那么就要像01背包那样仔细去考虑如何去循环 第一重循环我们从后向前遍历 因为我们需要s[i+1][j]; 第二重循环我么从前向后遍历 因为我们需要s[i][j-1]; 关于上述内容的证明可以参考 (https://www.cnblogs.com/ybwowen/p/11116654.html)
#include<bits/stdc++.h> using namespace std; const int maxn=1005; int dp[maxn][maxn]; int dp2[maxn][maxn]; int n; int a[maxn]; int sum[maxn]; int p[maxn][maxn]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i+n]=a[i]; for(int i=1;i<=2*n;i++) sum[i]=sum[i-1]+a[i],p[i][i]=i; for(int i=n<<1;i>=1;i--) for(int j=i+1;j<=n<<1;j++){ dp[i][j]=0x3f3f3f3f; dp2[i][j]=max(dp2[i][i]+dp2[i+1][j],dp2[i][j-1]+dp2[j][j])+sum[j]-sum[i-1]; for(int k=p[i][j-1];k<=p[i+1][j];k++) if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j]){ dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]; p[i][j]=k; }else if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]==dp[i][j]) p[i][j]=max(p[i][j],k); } int ans=0x3f3f3f3f; int ans2=0; for(int i=1;i<=n;i++) ans=min(ans,dp[i][i+n-1]); for(int i=1;i<=n;i++) ans2=max(ans2,dp2[i][i+n-1]); printf("%d\n%d\n",ans,ans2); return 0; }
另外的一些关于四边形优化的知识可参考https://www.jianshu.com/p/3a5b7d8e007f
标签:洛谷,int,石子,P1880,循环,maxn,include,我们,dp 来源: https://www.cnblogs.com/delta-cnc/p/12353444.html