题目链接
https://www.luogu.com.cn/problem/P1880
题目描述
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。
分析:其实这道题特别有意义的地方就是圆形操场,也就是说它的首尾也可以看作是相邻的,那么这道题我们就需要用到化圆为链的技巧了,
状态转移方程不难推导,先合并区间长度小的,然后再合并大区间,根据已有的小区间的最优值去更新大区间的最优值,出了这些没啥想说的,直接上代码,复杂度O(n^3)
#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1005;
int f1[Maxn][Maxn];
int f2[Maxn][Maxn];
int sum[Maxn];
int a[Maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i+n] = a[i];/*化圆为链*/
}
sum[0] = 0;
for(int i=1;i<=2*n;i++)
{
sum[i] = sum[i-1]+a[i];
}
memset(f2,0x7f7f7f7f,sizeof(f2));
for(int i=1;i<=2*n;i++) f2[i][i] = 0;
for(int len = 2;len<=n;len++)
for(int i=1;i+len-1<=2*n;i++)
for(int j=i;j<i+len-1;j++)
{
int k = i+len-1;
int temp = sum[k]-sum[i-1];
f1[i][k] = max(f1[i][k],f1[i][j]+f1[j+1][k]+temp);
f2[i][k] = min(f2[i][k],f2[i][j]+f2[j+1][k]+temp);
}
int Max = 0;
int Min = 0x7f7f7f7f;
for(int i=1;i<=n;i++)
{
Max = max(Max,f1[i][i+n-1]);
Min = min(Min,f2[i][i+n-1]);
}
printf("%d\n%d",Min,Max);
return 0;
}