Description
给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列,
Input
若干行,每行表示一组数据。对于每组数据,首先输入一个整数N,表示序列的长度。之后N个整数表示这个序列。
Output
同输入行数。对于每组数据,如果存在一种划分,则输出“Yes!”,否则输出“No!“。
Sample Input
6 3 1 4 5 8 7
6 3 2 1 6 5 4
Sample Output
Yes!
No!
HINT
共三组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9
第一组(30%):N <= 20
第二组(30%):N <= 100
第三组(40%):N <= 2000
题解
在前$i$位中找长$j$位的以第$i$位结尾的上升子序列(我们设这个为序列$A$),并且剩下的也是上升子序列(设这个为序列$B$),那么$f[i][j]$表示剩下的(即前i位中长$i-j$位的不以第$i$位结尾的)上升子序列(即序列$B$)的最后一位的最小值。
注意:$A$一定是以最后一个数结尾的。
然后转移:
如果$a[i]<a[i+1]$,那么$f[i+1][j+1] = min(f[i+1][j+1], f[i][j])$,意思就是将$a[i+1]$接在序列$A$后,相当于可以直接把$f[i][j]$扩展到第$i+1$位。
如果$f[i][j]<a[i+1]$,那么$f[i+1][i-j+1] = min(f[i+1][i-j+1], a[i])$,意思是将$a[i+1]$接在$B$后,相当于第$i+1$继承前$i$位中$i-j$位长的上升子序列,此时$B$序列的某尾为最后一个数,那么我们就要$swap(A,B)$。
1 //It is made by Awson on 2017.9.27 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <string> 9 #include <cstdio> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define Min(a, b) ((a) < (b) ? (a) : (b)) 16 #define Max(a, b) ((a) > (b) ? (a) : (b)) 17 #define LL long long 18 using namespace std; 19 const int N = 2000; 20 void read(int &x) { 21 char ch; bool flag = 0; 22 for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); 23 for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); 24 x *= 1-2*flag; 25 } 26 27 int n, a[N+5]; 28 int f[N+5][N+5]; 29 30 void work() { 31 for (int i = 1; i <= n; i++) 32 read(a[i]); 33 memset(f, 127, sizeof(f)); 34 int INF = f[0][0]; 35 f[1][1] = -1; 36 for (int i = 1; i <= n; i++) 37 for (int j = 0; j <= i; j++) 38 if (f[i][j] != INF) { 39 if (a[i] < a[i+1]) f[i+1][j+1] = Min(f[i+1][j+1], f[i][j]); 40 if (f[i][j] < a[i+1]) f[i+1][i+1-j] = Min(f[i+1][i+1-j], a[i]); 41 } 42 printf(f[n][n/2] == INF ? "No! " : "Yes! "); 43 } 44 int main() { 45 while (~scanf("%d", &n)) 46 work(); 47 return 0; 48 }