石子合并优化
题目描述
在一个园形操场的四周摆放N
堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2
堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1
个算法,计算出将N
堆石子合并成1
堆最大得分.
输入格式
数据的第1
行试正整数N,1≤N≤2000
,表示有N
堆石子.第2
行有N
个数,分别表示每堆石子的个数.
输出格式
输出共1
行,最大得分
样例
样例输入
4
4 4 5 9
样例输出
54
思路分析
-
看似是一道裸的区间dp,但关键是数据范围,如果找板子写,n3 的效率不炸才怪嘞,优化是必须的
-
主要问题在转移方程这里(该带佬发功了):
-
最大值有一个性质,总是在两个端点的最大者中取到。
-
说一下我的思路:
区间dp是在不多扩展宽度的,宽度大的,其值一定可以做到比它区间小的值要大,这样我们在扩张区间宽度时,最大值在此基础上再次处理即可
-
-
转移方程:
f[i][j]=max(f[i][j-1],f[i+1][j])+sum[j]-sum[i-1]
; -
另外还有四边形不等式优化,但是现对复杂,转载一下:https://blog.csdn.net/noiau/article/details/72514812
-
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=4010,INF=0x3f3f3f3f;
int n,m,f[maxn][maxn],ans,a[maxn],sum[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){ //环形就拓展长度即可
scanf("%d",&a[i]);
a[i+n]=a[i];
sum[i]=a[i]+sum[i-1];
}
for(int i=n+1;i<=2*n;i++){
sum[i]=sum[i-1]+a[i];
}
for(int d=2;d<=n;d++){ //枚举区间长度
for(int i=1,j;(j=i+d-1)<=n*2;i++){
f[i][j]=max(f[i][j-1],f[i+1][j])+sum[j]-sum[i-1];
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,f[i][i+n-1]);
}
printf("%d",ans);
return 0;
}