标签:
去年多校的题,很久以前就想补了,奈何智商有限,当初连题解代码都看不懂。
—————————————————————————————————————————————————————————————————————
题意转换为:给定n个数,求两个最长的不相交的LIS。
先说经典题一个LIS的nlogn做法。枚举当前数,若比末尾数大,插入末尾,否则二分查找,插入合适位置。
通过此题,我们有了一个用树状数组或线段树+dp解决LIS的方法。
首先离散化。dp[n]表示末尾数为n时的LIS的长度。
for循环枚举数列中的数i,dp[i] = max(dp[j])+1, j <= i
max的值可以用树状数组或线段树logn复杂度解决,总复杂度依然是nlogn。
咦,用树状数组也能解决?
树状数组两个操作,单点修改,前缀查询。
我们发现,dp[i]的值是单调不减的。每次修改,都可以用树状数组向上传递更新。(如果dp[i]的值会变小,就无法向上传递更新了。)
而我们查询的满足j <= i刚好是前缀查询。
树状数组完美解决。
—————————————————————————————————————————————————————————————————————
言归正传。
两个LIS如何解决?
dp[i][j]表示当前两个LIS末尾为i和j时的dp值。
dp[i][j] = max(dp[i][k])+1, k <= j;
我们只要先将答案存在一个数组里,然后统一更新dp数组的行值与列值即可。
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 7 struct p{ 8 int h, d; 9 p(){} 10 p(int h, int d):h(h), d(d){} 11 bool operator <(const p& x) const{ 12 return h != x.h? h > x.h : d < x.d; 13 } 14 }; 15 p pp[1005]; 16 int s[2005]; 17 int dp[1005][1005], tmp[1005]; 18 19 void add(int* a, int x, int d){ 20 for(int i = x; i < 1005; i += i&-i) 21 a[i] = max(a[i], d); 22 } 23 int sum(int* a, int x){ 24 int ret = 0; 25 for(int i = x; i; i -= i&-i) 26 ret = max(ret, a[i]); 27 return ret; 28 } 29 30 int main(){ 31 int t, n; scanf("%d", &t); 32 while(t--){ 33 scanf("%d", &n); 34 for(int i = 0; i < n; i++){ 35 scanf("%d%d", &pp[i].h, &pp[i].d); 36 s[i] = pp[i].d; 37 } 38 sort(pp, pp+n); 39 sort(s, s+n); 40 for(int i = 0; i < n; i++) 41 pp[i].d = lower_bound(s, s+n, pp[i].d)-s+1; 42 43 memset(dp, 0, sizeof(dp)); 44 int ans = 0; 45 for(int i = 0; i < n; i++){ 46 int v = pp[i].d; 47 for(int j = 1; j <= n; j++){ 48 tmp[j] = sum(dp[j], v)+1; 49 ans = max(ans, tmp[j]); 50 } 51 for(int j = 1; j <= n; j++){ 52 add(dp[j], v, tmp[j]); 53 add(dp[v], j, tmp[j]); 54 } 55 } 56 printf("%d\n", ans); 57 } 58 return 0; 59 }
hdu5406 CRB and Apple dp+两个LIS
标签:
原文地址:http://www.cnblogs.com/dirge/p/5636316.html