P1880 [NOI1995] 石子合并
题目:
https://www.luogu.com.cn/problem/P1880
题目描述
在一个圆形操场的四周摆放 NN 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 NN 堆石子合并成 11 堆的最小得分和最大得分。
输入格式
数据的第 11 行是正整数 NN,表示有 NN 堆石子。
第 22 行有 NN 个整数,第 ii 个整数 a_iai 表示第 ii 堆石子的个数。
输出格式
输出共 22 行,第 11 行为最小得分,第 22 行为最大得分。
区间dp
把环形链破坏成两条首位相连的链,注意要把所有的都换成n+n:
第一维枚举子区间的长度
第二维枚举左端点
第三维枚举断点
(ps:不能枚举用只用两层循环来枚举左右端点,因为那样无法保证最优子结构,而上面的三重循环则从小区间到大区间过渡)
//石子合并,只能合并相邻的
//每次合并的得分是两堆石子的和
//先考虑无环的情况
//枚举断点k
//f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[i][j]);
//sum[i][j]用前缀和来写,=s[j]-s[i-1];
//如果有环,就破环成链,相当于把数列加倍
// 把环形链破坏成两条首位相连的链,注意要把所有的都换成n+n:
#include<stdio.h> #include<algorithm> #include<string.h> #include<queue> using namespace std; typedef pair<int ,int > p; typedef long long ll; int t,m; int a[204]; int s[204]; int dp[204][204]; int d[204][204]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; } for(int i=1;i<=n;i++) { a[n+i]=a[i]; s[n+i]=s[n+i-1]+a[i]; } for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; for(int len=2;len<=n;len++) { for(int i=1,j=i+len-1;i<=n+n&&j<=n+n;i++,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]+s[j]-s[i-1]); if(d[i][j]==0) d[i][j]=d[i][k]+d[k+1][j]+s[j]-s[i-1]; else d[i][j]=min(d[i][j],d[i][k]+d[k+1][j]+s[j]-s[i-1]); } } } int mmax=0; int mmin=0x3f3f3f3f; for(int i=1;i+n-1<=n+n;i++) { mmax=max(mmax,dp[i][i+n-1]); mmin=min(mmin,d[i][i+n-1]); } printf("%d\n",mmin); printf("%d\n",mmax); }
浙公网安备 33010602011771号