P4728 [HNOI2009]双递增序列

题意

这个DP状态有点神。

首先考虑一个最暴力的状态:(f_{i,j,k,u})表示第一个选了(i)个,第二个选了(j)个,第一个结尾为(k),第二个结尾为(u)是否可行。

现在考虑消减状态:

1.首先知道了处理到第几个,那么只要知道一个长度就能推出另一个。 因此状态可以改为(f_{i,j,k,u})表示处理到了第(i)个,第一个序列选了(j)个,第一个序列结尾为(k),第二个序列结尾为(u)是否可行。(这并没有减少维数,只是转化下,方便处理。)

2.既然所有的数都要选,假设当前处理到了第(i)个,那么第(i-1)个必定在结尾,因此我们可以消去一维:
(f_{i,j,k})表示处理到第(i)个,结尾是(a_i)的那个序列长度为(j),另一个结尾为(k)是否可行。

3.贪心地想,一个序列结尾越小越容易接数,因此可以将一维放到DP的值中:
(f_{i,j})表示处理到第(i)个,结尾是(a_{i-1})的那个序列长度为(j),另一个结尾最小是多少。

现在我们已经将DP减到二维,于是就可以转移了:

(a_i>a_{i-1}):此时(a_i)可以拼接在(a_{i-1})后面:
(f_{i,j}=min(f_{i,j}f_{i-1,j-1}))

(a_i>f_{i-1,i-j}):此时我们可以将(a_i)接到另一个序列后面,于是我们交换两个序列,因为另一个序列后面接了(a_i),我们要符合定义,于是可得:
(f_{i,j}=min(f_{i,j},a_{i-1}))

最后判断(f_{n,n/2})是否为(inf)

数据范围不知道,到某dark上看了看,(nleqslant2000)

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2010;
int T,n;
int a[maxn];
int f[maxn][maxn];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		memset(f,0x3f,sizeof(f));
		f[0][0]=-1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=min(n/2,i);j++)
			{
				if(a[i]>a[i-1])f[i][j]=min(f[i][j],f[i-1][j-1]);
				if(a[i]>f[i-1][i-j])f[i][j]=min(f[i][j],a[i-1]);
			}
		puts(f[n][n/2]<0x3f3f3f3f?"Yes!":"No!");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/nofind/p/12068936.html