*题解: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;
}

浙公网安备 33010602011771号