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

浙公网安备 33010602011771号