luogu1880:石子合并:区间DP元问题

题目连接

  • 该题是luogu试炼场的2-16:T4

题目大意

  1. 有n堆石子,围成一圈;(成环)
  2. 单次只能合并相邻的两堆石子,消耗与“两堆石子”等重的能量;
  3. 要求把所有石子合并完成,消耗的能量最小。

题目分析

  • 区间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
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;
}



你可能感兴趣的:(题解,大礼包,luogu,DP)