石子问题(区间dp)
码蹄集OJ-石子合并
#include<bits/stdc++.h>
using namespace std;
const int N=202;
int n,a[N],sum[N],dp[N][N];
int main( )
{cin>>n;for(int i=1;i<=n;i++){cin>>a[i];a[i+n]=a[i];}for(int i=1;i<=2*n;i++){sum[i]=sum[i-1]+a[i];}for(int i=1;i<=2*n;i++){for(int j=1;j<=2*n;j++){if(i==j){dp[i][j]=0;}else{dp[i][j]=-1e8;}}}for(int len=2;len<=n;len++){for(int i=1;i+len-1<=2*n;i++){int j=i+len-1;for(int k=i;k<j;k++){dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);}}}int result=0;for(int i=1;i<=n;i++){result=max(result,dp[i][i+n-1]);}cout<<result;return 0;
}
将数组的大小扩大一倍(即把原数组复制一份接在后面),将环形问题转换成线性问题解决。
任何环形区间的操作,都能在展开后的双倍线性数组上,通过一个连续的线性区间来表示。
定义dp[i][j]数组表示将i到j之一段石子合并成一堆时的得分。
初始化dp数组,在区间范围为0时,dp数组大小为0。由于题中求的是最小值,其他dp数组定义为无穷小。
枚举区间大小,再枚举起点,这样终点的值可以被确定。定义一个中间值k,遍历从起点到终点的所有值,得出状态转移方程:
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
因为在石子问题中,总值=之前的值+当前的值,当前的值是确定的,通过前面算的累加数组得到起点到终点的值sum[j]-sum[i-1]。之前的值就通过枚举中间值k来表示。
注意:因为题目是环形数组,所以结果不能直接输出dp[1][j]。应该遍历所有起点,得出的最大值才是结果。