根据数据范围,不难想到 DP 状态应该是 \(n^4\) 级别的。
先考虑当没有反转区间的操作时如何转移。
设 \(dp_{l,r,L,R}\) 表示当前区间为 \(l\sim r\),值域 \(\in [L,R]\) 时的答案。转移时枚举四个维度,可以从 \(dp_{l,r,L,R-1},dp_{l,r,L+1,R},dp_{l+1,r,L,R},dp_{l,r-1,L,R}\) 转移过来。
加上翻转操作后,我们思考其本质。翻转一个子序列可以理解为交换某几对数字的位置,这样的话相当于如果 \(a_l=R\) 或者 \(a_r=L\) 的话,我们可以通过翻转 \(l\sim r\) 中的任意一个包含 \(l,r\) 的子序列来满足条件,即 \(dp_{l,r,L,R}=dp_{l+1,r-1,L,R}+[a_l=R]+[a_r=L]\)。
由于区间 DP 按照区间从小到大的顺序,故可以保证这样的翻转满足题目条件,所以这道题就结束了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int w=1,s=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
return w*s;
}
const int mod=998244353;
const int maxn=51;
const int inf=3e17+7;
int n,a[maxn],dp[maxn][maxn][maxn][maxn];
signed main()
{
#ifdef Lydic
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
// #else
// freopen("Stone.in","r",stdin);
// freopen("Stone.out","w",stdout);
#endif
cin>>n;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)
for(int l=1;l<=a[i];l++)
for(int r=a[i];r<=50;r++)
dp[i][i][l][r]=1;
for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
for(int lenn=1;lenn<=50;lenn++)
{
for(int L=1;L<=50-lenn+1;L++)
{
int R=L+lenn-1;
dp[l][r][L][R]=max(dp[l][r][L+1][R],dp[l][r][L][R-1]);
dp[l][r][L][R]=max(dp[l][r][L][R],dp[l+1][r][L][R]+(a[l]==L));
dp[l][r][L][R]=max(dp[l][r][L][R],dp[l][r-1][L][R]+(a[r]==R));
dp[l][r][L][R]=max(dp[l][r][L][R],dp[l+1][r-1][L][R]+(a[l]==R)+(a[r]==L));
}
}
}
}
cout<<dp[1][n][1][50];
return 0;
}
标签:ch,freopen,int,USACO17JAN,Subsequence,maxn,Reversal,dp,翻转
From: https://www.cnblogs.com/Lydic/p/18510224