P1410 子序列
链接:https://www.luogu.com.cn/problem/P1410
题意
给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列。
题解
先考虑暴力dp,如果设状态为\(dp[i][j][l][k][r]\)表示前i项中其中一个长度为j,以l结尾,另一个长度为k,以r结尾是否可行,那这道题就做完了。(可惜\(n <= 2000\))
考虑显然优化,因为\(j+k=i\)并且两个当中必然有一个以\(a[i]\)结尾,所以可以优化为\(dp[i][j][k]\)表示前i项以a[i]结尾的严格递增子序列长度为\(j\)是否可行,另外一个以\(k\)结尾,就优化为了\(O(n^3)\)
考虑贪心,在\(i,j\)相同的情况下,\(dp[i][j][k]\)由最小的\(k\)转移显然是最优的(k越小\(dp[i][j][k]\)越容易转为\(true\)),而且我们可以考虑把贪心的一维压入\(dp\)值中,于是最终状态为\(dp[i][j]\)表示前i项以a[i]结尾的严格递增子序列长度为j,另一个的结尾的最小值。
方程
\(dp[i][j] = min(dp[i-1][i-1]) if:a[i]>a[i-1]\)
考虑把a[i]放入以a[i - 1]结尾的序列
\(dp[i][i-j+1] = min(a[i - 1])if:a[i] > dp[i - 1][j - 1]\)
考虑把a[i]放入不以a[i - 1]结尾的序列
代码(如果不理解请仔细考虑状态的意思)
#include <bits/stdc++.h>
using namespace std;
#define R register
typedef long long ll;
const int MAXN = 2e3 + 5;
int a[MAXN];
int dp[MAXN][MAXN];
int main()
{
int n;
while(cin >> n)
{
for(R int i = 1;i <= n; i++)
scanf("%d", &a[i]);
memset(dp,0x3f,sizeof(dp));
dp[1][1] = 0;
for(R int i = 2;i <= n; i++)
{
for(R int j = 1;j <= i; j++)
{
if(a[i] > a[i - 1])dp[i][j] = min(dp[i][j],dp[i - 1][j - 1]);
if(a[i] > dp[i - 1][j - 1])dp[i][i - j + 1] = min(dp[i][i - j + 1],a[i - 1]);
}
}
if(dp[n][n / 2] != 0x3f3f3f3f)printf("Yes!\n");
else printf("No!\n");
}
return 0;
}