[HNOI2009]双递增序列(动态规划,序列dp)

感觉这个题还蛮难想的.
首先状态特别难想.设\(dp[i][j]\)表示前i个数,2序列的长度为j的情况下,2序列的最后一个数的最小值.
其中1序列为上一个数所在的序列,2序列为另外一个序列.
这样设状态的巧妙之处在于,它几乎完美地用最精炼的语言描述了序列的信息,使我们可以方便地转移.我们现在知道1序列的最后一个数\(seq[i]\),2序列的最后一个数\(dp[i][j]\).1序列的长度\(i-j\),2序列的长度\(j\).
于是转移就是:
如果可以接在1序列上的话,即\(seq[i-1]<seq[i]\),那么\(dp[i][j]=min(dp[i][j],dp[i-1][j])\)
如果可以接在2序列上的话,即\(dp[i-1][i-j]<seq[i]\)(可以接在2序列上,但是由于状态是2序列的长度,所以两个序列要交换,所以是i-j),那么1序列和2序列要互换,此时2序列的最后一个元素就是之前1序列的最后一个元素\(seq[i-1]\),即\(dp[i][k]=min(dp[i][j],seq[i-1])\)

#include<bits/stdc++.h>
#define maxn 2005
using namespace std;
int seq[maxn],n;
int dp[maxn][maxn];
//dp[i][j]表示前i个数,2序列长度为j,2序列最后一个数的最小值.
int main()
{
	int T;cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)scanf("%d",&seq[i]);
		memset(dp,0x3f,sizeof(dp));dp[0][0]=-1;
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<=min(n/2,i);j++)
			{
				if(seq[i-1]<seq[i])dp[i][j]=dp[i-1][j];
                //大于前一个数,说明可以直接接在前一个数之后,2序列的最后元素不需要变大
				if(dp[i-1][i-j]<seq[i])dp[i][j]=min(dp[i][j],seq[i-1]);
				//大于2序列的最后一个数,说明可以接在2序列之后,此时1序列和2序列交换
				//本来作为数组里的值表示的现在变成下标体现,原本下标体现的现在变成值
				//1序列表示上一次的数所在的序列,2序列是另一个序列
				//seq[i-1]是1序列最后一个元素
			}
		}
		if(dp[n][n/2]<=1e6)printf("Yes!\n");
		else printf("No!\n");
	}
	return 0;
}

posted @ 2018-10-29 22:35  Brioche  阅读(328)  评论(0编辑  收藏  举报