题目链接:https://www.luogu.com.cn/problem/P3146
这题一开始我想的状态是f[i][j]表示从i合并到j的最大值,方程就是如果f[i][l]=f[l+1][j],f[i][j]=max(f[i][j],f[i][l]+1);否则f[i][j]=max(f[i][j],max(f[i][l],f[l+1][j]));
但这是不对的,因为后面一个式子可能导致f[i][j]得出了一个值,但这个值是从子区间里选的,即使f[i][j]和某个区间相等也不一定能合并。举个例子:3 1 2 2-->3 1 3是正确的合并,答案为3,但方程会这样执行: 3 1 2 2-->3 1 3-->3 3-->4。 于是又沙雕的开了一个数组v,v[i][j]=1表示能从i合并到j,v[i][j]=0表示不能,每次合并前先判断一下子区间的v是不是为1。脑补一下感觉问题不大,实际上也过了。
#include<bits/stdc++.h> using namespace std; const int maxn=250+10; int f[maxn][maxn],v[maxn][maxn],a[maxn]; int n,i,j,k,t,m,l; int main(){ cin>>n; for (i=1;i<=n;i++) cin>>a[i]; memset(f,0,sizeof(f));memset(v,0,sizeof(v)); for (i=1;i<=n;i++) { f[i][i]=a[i];v[i][i]=1; } for (k=1;k<=n-1;k++) //*** for (i=1;i<=n-1;i++) if (i+k<=n){ j=i+k; for (l=i;l<=j-1;l++) if (v[i][l]==1&&v[l+1][j]==1){ //若都是完全合并,则分为两堆相等和两堆不等 if (f[i][l]==f[l+1][j]){ if (f[i][l]+1>=f[i][j]){ f[i][j]=f[i][l]+1;v[i][j]=1; } } else{ m=max(f[i][l],f[l+1][j]); if (m>f[i][j]){ f[i][j]=m;v[i][j]=0; } } } else{ //如果不都是完全合并 m=max(f[i][l],f[l+1][j]); if (m>f[i][j]){ f[i][j]=m;v[i][j]=0; } } } cout<<f[1][n]<<endl; return 0; }
后来在洛谷上看题解时,看到一种更好的状态,是f[i][j]表示从i合并到j且全部合并,能得到的最大值。因此f[i][j]可以为0(i到j不能全部合并)
转移方程:若f[i][l]=f[l+1][j](l为分割点),f[i][j]=max(f[i][l]+1,f[i][j]),并同时更新答案ans=max(ans,f[i][j])。
#include<bits/stdc++.h> using namespace std; const int maxn=250+10; int a[maxn],f[maxn][maxn]; //f[i][j]表示将i~j *全部* 合并能得到的最大值 int n,i,j,k,ans,l; int main(){ cin>>n; ans=0;memset(f,0,sizeof(f)); for (i=1;i<=n;i++){ cin>>a[i];f[i][i]=a[i]; ans=max(ans,a[i]); } for (k=1;k<=n-1;k++) for (i=1;i<=n-1;i++) if (i+k<=n){ j=i+k; for (l=i;l<=j-1;l++) if (f[i][l]==f[l+1][j]&&f[i][l]!=0&&f[l+1][j]!=0){ //若f[i][l]=0;说明区间[i,l]不能全部合并,f[i+1][j]同理 f[i][j]=max(f[i][j],f[i][l]+1); ans=max(ans,f[i][j]); //随时更新答案,因为答案不一定为f[1][n] } } cout<<ans<<endl; return 0; }