石子合并(弱化版)

发布时间 2023-06-10 09:45:55作者: Momo·Trace

石子合并(弱化版)

题目描述

设有 \(N(N \le 300)\) 堆石子排成一排,其编号为 \(1,2,3,\cdots,N\)。每堆石子有一定的质量 \(m_i(m_i \le 1000)\)。现在要将这 \(N\) 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

输入格式

第一行,一个整数 \(N\)

第二行,\(N\) 个整数 \(m_i\)

输出格式

输出文件仅一个整数,也就是最小代价。

样例

样例输入 #1

4
2 5 3 1

样例输出 #1

22

解析

\(dp[i][j]\)表示区间\([i,j]\)的最小价值。

不妨从终点考虑问题,即结果为两个子区间合并的最小值再加上合并需要的代价即可。

枚举两个子区间,即枚举这个区间的中间点\(k\),使这个区间被分为\([i,k]\)\([k+1,j]\)两个区间,取一遍最小值加上合并的即为当前区间所求。

至于合并的代价,用前缀和即可。

所以$$dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])$$

代码

#include<bits/stdc++.h>
using namespace std;
int dp[310][310],a[310],sum[310];
int main()
{
	int n;
	cin>>n;
	memset(dp,0x3f,sizeof(dp));//初始化1,因为是求最小代价,所以初始化设为很大的一个数,为了后面更新。
	for(int i=1;i<=n;i++)
	{
		cin >> a[i];
		sum[i]=sum[i-1]+a[i];
		dp[i][i]=0;//初始化2,他自己本身的代价为0。
	}
	for(int len=2;len<=n;len++)
	{
		for(int i=1;i<=n-len+1;i++)
		{
			int j=i+len-1;
			for(int k=i;k<j;k++)
			{
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
	cout<<dp[1][n];
}