模型
https://www.luogu.org/problemnew/show/P1880
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
得出方程:O(n^3)
四边形优化:O(n^2)
四边形方程:
- 证明cost满足上式;
- cost成立时,dp也会满足上式;
- 设
,表示
的最优决策为
。由
得出
单调:
- 。
- 。
- 那么做 时,只需要从 枚举到 即可。
解题
- ,成立;
- 对于时间复杂度不够的类似二维dp,推荐 打表验证,改成四边形优化很方便的;(绝对不是懒得证明 hahaha)
- 在更新 的时候维护 数组即可。
当然,取最大值显然是一个一个合并最大了。
环形只需要后面再来n个就行
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL a[209];
LL dp[209][209];
int best[209][209];
LL ma[209][209];
int main(){
int n;scanf("%d",&n);
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++){
scanf("%lld",a+i);
a[i]+=a[i-1];
}
for(int i=n+1;i<=2*n;i++){
a[i]=a[i-1]+a[i-n]-a[i-n-1];
}
n*=2;
for(int i=1;i<n;i++){
best[i][i+1]=i;
ma[i][i+1]=dp[i][i+1]=a[i+1]-a[i-1];
}
for(int i=1;i<=n;i++){
dp[i][i]=0;
}
LL ans1=1e18,ans2=0;
for(int len=3;len<=n/2;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
ma[i][j]=max(ma[i][j-1],ma[i+1][j])+a[j]-a[i-1];
for(int k=best[i][j-1];k<=best[i+1][j];k++){
LL tmp=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];
if(tmp<dp[i][j]){
dp[i][j]=tmp;
best[i][j]=k;
}
}
if(len==n/2)ans1=min(ans1,dp[i][j]),ans2=max(ans2,ma[i][j]);
}
}
printf("%lld\n%lld\n",ans1,ans2);
}