[区间dp][CCC 2016]合并饭团

题意

\(N\)个数,相邻数字若相同可以合并,合并后数字为原来两数之和,位置与两数位置相同。若两个相等数字之间有一个数字,则也可以合并,合并后数字大小为原来三数之和,位置与原来三数相同。求可以得到的最大数字。
\(1\le N \le 400\)

分析

一眼区间dp,\(f[l][r]\)表示合并第i个到第j个数字可以得到的最大数字的值。

\(f[l][r]=max(f[l][r],f[l][k]+f[k+1][r])\ (l\le k \le r-1,f[l][k]=f[k+1][r])\)

\(f[l][r]=max(f[l][r],f[l][k]+f[w][r]+f[k+1][w-1])\ (r-l+1\ge 3,f[l][k]=f[w][r],l\le k\le w-2 \le r)\)


若只处理两数相邻的情况,则和石子合并相同,复杂度\(O(N^{3})\)。若需处理三数相同的情况,使用朴素算法(在枚举长度和区间左端点后再枚举三个数的两个分界点)达到了\(O(N^{4})\)的复杂度,显然是不能接受的。
进一步分析,在三数相同的分支中,当\(k\)增大,\(f[l][k]\)随之增大,具有单调性,同理\(f[w][r]\)具有单调性,故可双指针枚举\(k,w\)降低一维复杂度。具体见代码。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=410;
int a[N],f[N][N],n,ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){cin>>a[i];f[i][i]=a[i];ans=max(ans,a[i]);}
	for(int len=2;len<=n;len++)
		for(int l=1;l+len-1<=n;l++)
		{
			int r=l+len-1;
			int w=r;
			for(int k=l;k<=r;k++)
			{
				if(f[l][k]==f[k+1][r]&&k+1<=r) 
                                        f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);
				if(len>=3)
				{
					while(f[l][k]>f[w][r]&&w>k+2) w--;
					if(f[l][k]==f[w][r]&&f[k+1][w-1]!=0) 
						f[l][r]=max(f[l][r],f[l][k]+f[w][r]+f[k+1][w-1]);
				}
				
				
			}
			ans=max(ans,f[l][r]);
		}
	cout<<ans;
	
}
posted @ 2022-04-16 21:14  Hssliu  阅读(84)  评论(0)    收藏  举报