*题解:P4728 [HNOI2009] 双递增序列

题目链接

解析

如果不考虑长度限制或者是看错题目,就可以设 \(f_{i,j}\) 表示当前考虑到第 \(j\) 个数,两个序列其中一个最后元素的下标为 \(i\) 是否可行。可以发现,此时另一个序列的最后元素下标应该为 \(j\)。转移是简单的。

现在有了长度限制,需要把长度加进状态里。设 \(f_{i,j,k}\) 额外表示 \(a_j\) 所在序列的长度为 \(k\) 即可。但这样仍然不够。注意到我们的 \(f\) 里只存了真和假两种值,考虑让它存一些更有用的东西,比如说我们想优化掉的第一维:设 \(f_{j,k}\) 表示考虑到第 \(j\) 个数,包含 \(a_j\) 的那个序列长度为 \(k\),另一个序列的末尾数字最小值。于是:

\(a_j > a_{j - 1}\) 则说明可以接上 \(a_{j - 1}\),另一个序列的末尾最小值仍旧是 \(f_{j - 1,k - 1}\)

\[f_{j,k}\leftarrow \min(f_{j,k},f_{j - 1,k - 1}) \]

对于接上另一个序列的情况,需要 \(a_j > f_{j - 1,j - k}\)\(a_{j - 1}\) 结尾序列长度为 \(j\),由于接了另一个序列,当前 \(f_{i,k}\) 中的 \(k\) 对应的是另一个序列长度,所以应该从序列长度为 \(k - 1\) 的状态转移,即 \(f_{j - 1,j - 1 - (k - 1)}=f_{j - 1,j - k}\)),此时 \(a_{j - 1}\) 作为另一个序列的末尾数字:

\[f_{j,k}\leftarrow \min(f_{j,k},a_{j - 1}) \]

\(f_{n,\frac{n}{2}}\) 即为所求。

时间复杂度 \(O(n ^ 2m)\)

代码

代码中采用了另一种本质相同的状态设计,两个维度都设为序列的长度。

由于 \(a_i\) 有可能是 \(0\),所以要注意初始化。

/*
*/
#include <bits/stdc++.h>
#define mid ((1ll * l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2000 + 5,M = 10 + 5,L = 19,L2 = 17,mod = 11380; 
int f[N][N],a[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout); 
	int m;
	cin>>m;
	while(m--){
		int n;
		cin>>n;
		memset(f,127,sizeof(f));
		f[0][0] = -1;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int sum = 1;sum <= n;sum++){
			for(int i=0;i<=sum;i++){
				int j = sum - i;
				if(j && a[i + j] > a[i + j - 1]){
					f[i][j] = f[i][j - 1];
				}
				if(j && a[i + j] > f[j - 1][i]){
					f[i][j] = min(f[i][j],a[i + j - 1]);
				}
			}
		}
		cout<<(f[n / 2][n / 2] < 2e9 ? "Yes!" : "No!")<<'\n';
	}
	return 0;
}
posted @ 2026-05-22 16:54  yutar  阅读(3)  评论(0)    收藏  举报