题目连接
- 该题是luogu试炼场的2-16:T4
题目大意
- 有n堆石子,围成一圈;(成环)
- 单次只能合并相邻的两堆石子,消耗与“两堆石子”等重的能量;
- 要求把所有石子合并完成,消耗的能量最小。
题目分析
- 区间DP的概念:要知道从i->j区间内的最优解,则需要枚举区间内的所有情况;
- 本题成环,所以需要用两倍的长度作为过程,最后只需要扫一遍n长度就可以了。
解题思路
- f[i][j]表示从i到j号石子合并需要花费的最少能量;
- 枚举k|[i,j):f[i][j]=min(f[i][k],f[k+1][j])+s[i][j];//枚举左右区间,k是分界
- 还需要枚举区间的长度:从小到大堆积出答案;
- 本题代码只打了暴力,据说还能用“四边形不等式优化”
代码:
暴力求
//luogu1880:石子合并
//环形摆放,所以必须循环操作
//枚举左右的过程情况:最终得出答案
#include<bits/stdc++.h>
using namespace std;
int n, a[210],s[210],f[210][210];
void dd()//求最大合并花费
{
memset(f,0,sizeof(f));
for(int j=1;j<=n;j++) //枚举长度
{
for(int i=1;i<=2*n-j+1;i++) //枚举开头
{
int w=i+j-1; //当前区间的末尾
for(int x=i+1;x<=w;x++)
{
int su=s[w]-s[i-1];
f[i][j]=max(f[i][x-i]+f[x][w-x+1]+su,f[i][j]);
}
}
}
int su=0;
for(int i=1;i<=n;i++)
{
su=max(su,f[i][n]);
}
printf("%d\n",su);
}
void xx()//求最小合并花费
{
memset(f,63,sizeof(f));
for(int i=1;i<=2*n;i++) f[i][1]=0;
for(int j=1;j<=n;j++) //枚举长度
{
for(int i=1;i<=2*n-j+1;i++) //枚举开头
{
int w=i+j-1; //当前区间的末尾
for(int x=i+1;x<=w;x++)
{
int su=s[w]-s[i-1];
f[i][j]= min(f[i][x-i]+f[x][w-x+1]+su,f[i][j]);
}
}
}
int su=999999999;
for(int i=1;i<=n;i++)
{
su=min(su,f[i][n]);
}
printf("%d\n",su);
}
int main()
{
scanf("%d",&n);
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
for(int i=n+1;i<=2*n;i++)
{
a[i]=a[i-n];
s[i]=s[i-1]+a[i];
}
xx();
dd();
return 0;
}