P1880合并石子
题目链接:
题意分析:
这道题是区间Dp的经典题,对于一个区间内的石子,每次合并相邻的两堆石子,求出最大和最小得分。
思路分析:
Dp的状态表示可以从最后一步来思考,假设最后一步是合并已经合并完成的两堆石子,考虑最大值,f[i][j]表示区间[i,j]合并的最大值,枚举最后一步来得到转移方程,即最后合并的两堆是[i,k]和[k+1,r],其中i<=k<r.
Dp方程:f[i][j]表示合并区间[i,j]的最大值;
Dp转移:f[i][j]=max(f[i][k]+f[k+1][r]+sum[j]-sum[i-1],f[i][j]) i<=k<r, sum[i]表示前缀和。
坑点:
1.这道题是环形区间dp,一般的套路就是将区间[1,n]变为[1,2n],在[1,2n]中取区间长度为n的区间来代替环形的效果。
2.对于区间Dp,很多时候需要从长度较小区间来递推到长度较大区间,所以常见的枚举方式是从小到大枚举区间长度;
3.注意f[i][i]=0和一些初始值的初始化。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=310*2;
int f[N][N],g[N][N];
int s[N];
int w[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>w[i];
w[i+n]=w[i];
}
for(int i=1;i<=n*2;i++)
s[i]=s[i-1]+w[i];
memset(f,0x3f,sizeof(f));
memset(g,-0x3f,sizeof(g));
for(int i=1;i<=n*2;i++)
f[i][i]=g[i][i]=0;
for(int d=2;d<=n;d++){
for(int l=1;l+d-1<=2*n;l++){
int r=l+d-1;
for(int k=l;k<r;k++){
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
}
}
}
int res1=1e9,res2=0;
for(int i=1;i<=n;i++){
res1=min(res1,f[i][i+n-1]);
res2=max(res2,g[i][i+n-1]);
}
cout<<res1<<"\n";
cout<<res2<<"\n";
return 0;
}

浙公网安备 33010602011771号