石子合并(弱化版)

洛谷题面

题目大意

设有 \(N(N \le 300)\) 堆石子排成一排,其编号为 \(1,2,3,\cdots,N\)。每堆石子有一定的质量 \(m_i(m_i \le 1000)\)

现在要将这 \(N\) 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。

试找出一种合理的方法,使总的代价最小,并输出最小代价。

题目分析

辨别区间 \(\rm DP\) 的方式:

从小区间延伸到大区间(合并等方式)。

许多括号数数题就是区间 \(\rm DP\) 好题,今年 \(\verb!2021-CSP-S-括号序列!\) 不妨一做。


\(dp[l][r]\) 表示区间 \([l,r]\) 内合并的最小代价。

枚举左右区间,再枚举中间断点来判断是否答案会变得更优:

\(dp[l][r]=\min\{dp[l][k]+dp[k+1][r]+cost\}(l\le k\lt r)\)

其中 \(cost\) 表示 \(\sum\limits_{i=l}^{r}a_i\)

枚举区间(左右+断点)复杂度为 \(O(N^3)\),中间枚举价值的部分又是 \(O(N)\),总时间复杂度 \(O(N^4)\)

枚举价值部分可以通过前缀和优化,优化为 \(O(N^3)\)

代码

//2022/1/6

const int INF=0x3f3f3f3f;

const int ma=305;

int a[ma],sum[ma];

int dp[ma][ma];

int n;

int main(void)
{
	n=read();
	
	Input_Int(n,a);
	
	for(register int i=1;i<=n;i++)
	{
		sum[i]=sum[i-1]+a[i];
	}
	
	for(register int len=2;len<=n;len++)
	{
		for(register int l=1;l+len-1<=n;l++)
		{
			int r=l+len-1;
			
			dp[l][r]=INF;
			
			for(register int k=l;k<r;k++)
			{
				int w=sum[r]-sum[l-1];
				
				dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+w);
			}
		}
	}
	
	printf("%d\n",dp[1][n]);
	
	return 0;
}
posted @ 2022-01-06 22:01  Coros_Trusds  阅读(260)  评论(0编辑  收藏  举报