【题解】石子合并
区间dp模板题
题目要求一个环中的最小价值和最大价值(如题)。
首先,我们段环成链,即开两倍的空间,i与i+n对应。
用前缀和预处理出任意区间中的和。
接下来,我们枚举段环的位置。
设dp[i][j]是i到j合并的最小价值。
显然我们可以枚举k作为段环的点,方程即为:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+dis(i,j)),
就是将i到k的最小价值和k+1到j的最小价值与i到j的石子数量总和加起来,取最小。
设f[i][j]是i到j合并的最大价值,同理。
我们枚举i到j,并在i到j之间枚举k作为断点。
每次dp即可。
最后需要分别求一遍最大最小值。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<iostream> #define MAXN 2147483647 #include<cstring> using namespace std; int n,sum[50000]; int a[50000],minn,maxn; int f[500][500];//max int dp[500][500];//min inline int dis(int i,int j){return sum[j]-sum[i-1];} inline void read(int &x){ int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); } x=s*w; } int main(){ read(n); for(int i=1;i<=(n);++i){ scanf("%d",&a[i]); a[i+n]=a[i]; } for(int i=1;i<=(n<<1);i++) sum[i]=sum[i-1]+a[i]; for(int p=1;p<n;p++){ for(int i=1,j=i+p;(j<(n<<1))&&(i<(n<<1));i++,j=i+p){ dp[i][j]=MAXN; for(int k=i;k<j;++k){ dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+dis(i,j)); f[i][j] =max (f[i][j],f[i][k] +f [k+1][j]+dis(i,j)); } } } minn=MAXN; maxn=-MAXN; for(int i=1;i<=n;i++){ minn=min(dp[i][i+n-1],minn); maxn=max(f[i][i+n-1],maxn); } printf("%d\n%d",minn,maxn); return 0; }