区间DP好题,把区间DP的基本操作都用上了。(状态转移方程很多,见代码吧)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
const int N=310;
int t,n,m,a[N],d,dp[N][N],f[N];
bool can[N][N];
map<int,int>mp;
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
memset(f,0,sizeof(f));memset(can,0,sizeof(can));memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);mp.clear();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d",&d),mp[d]=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(mp[a[j]-a[i]])can[i][j]=1;
for(int len=1;len<=n;len++)
for(int l=1;l+len<=n;l++)
{
int r=l+len;
dp[l][r]=max(dp[l+1][r],dp[l][r-1]);//区间DP基操
if(can[l][r]&&dp[l+1][r-1]==(r-l-1))
dp[l][r]=max(dp[l][r],dp[l+1][r-1]+2);//如果端点构成等差,且中间取空了
for(int i=l;i<r;i++)//枚举分点,DP基操
dp[l][r]=max(dp[l][r],dp[l][i]+dp[i+1][r]);
for(int i=l+1;i<r;i++)
{
if(can[l][i]&&dp[l+1][i-1]==(i-l-1))//如果左边构成等差且左边取空了
dp[l][r]=max(dp[l][r],dp[l+1][i-1]+dp[i+1][r-1]+2);
if(can[i][r]&&dp[i+1][r-1]==(r-i-1))//如果右边构成等差且右边取空了
dp[l][r]=max(dp[l][r],dp[l][i-1]+dp[i+1][r-1]+2);
if(can[l][i]&&can[i][r]&&(a[i]-a[l]==a[r]-a[i])&&dp[l+1][i-1]==(i-l-1)&&dp[i+1][r-1]==(r-i-1))
//左中右构成等差,且左中右取空了
dp[l][r]=max(dp[l][r],r-l+1);
}
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
f[i]=max(f[i],f[j-1]+dp[j][i]),ans=max(ans,f[i]);
printf("%d\n",ans);
}
return 0;
}
总结
区间DP好好好题