区间DP
经典区间DP,和能量项链很像。
先把环断成链,再把链*2,就转化成了一个区间问题。
f[l][r]
表示把
[l,r]
这段石子合并所能达到的最大/小值。枚举断点i(
i∈(l,r)
),把原区间分成
[l,i]
和
[i+1,r]
两段求解。
转移方程:
f[l][r]=max(f[l][r],dp(l,i)+dp(i,r)+∑rj=lw[j])
记忆化搜索即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200
using namespace std;
int n;
int f1[MAXN+5][MAXN+5],f2[MAXN+5][MAXN+5],w[MAXN+5];
int dp(int l,int r,bool flag){
if (flag&&f1[l][r]) return f1[l][r];
if (!flag&&f2[l][r]) return f2[l][r];
if (l==r) return f1[l][r]=f2[l][r]=0;
f1[l][r]=0; f2[l][r]=0x7fffffff;
for (int i=l;i<r;i++){
f1[l][r]=max(f1[l][r],dp(l,i,1)+dp(i+1,r,1)+w[r]-w[l-1]);
f2[l][r]=min(f2[l][r],dp(l,i,0)+dp(i+1,r,0)+w[r]-w[l-1]);
}
if (flag) return f1[l][r];
else return f2[l][r];
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%d",&w[i]);
w[n+i]=w[i];
}
for (int i=1;i<=n*2;i++)
w[i]+=w[i-1];
dp(1,2*n,1); dp(1,2*n,0);
int ans1=0,ans2=0x7fffffff;
for (int i=1;i<=n;i++){
ans1=max(ans1,f1[i][n+i-1]);
ans2=min(ans2,f2[i][n+i-1]);
}
printf("%d\n%d\n",ans2,ans1);
return 0;
}