SGU 199 Beautiful People 二维最长递增子序列
题目链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20885
题意:
求二维最长严格递增子序列。
题解:
O(n^2)的算法很好想,不过这里会t掉,只能O(nlogn)
于是用二分来维护:
先把所有的数按x递增排序,x相同的按y递减排序(这里之所以要按y递减排序是因为为了写代码方便,递减的话你后面基本就只要考虑y的大小,如果不递减,你还要考虑x的大小的,具体的可以自己思考一下)
排完序之后我们接下来就只考虑y的大小,我们维护一个目前长度为len的最长子序列,并且它的value取目前所有长度为len的最长子序列中,以最小y结尾的那个y值。
然后我们会发现我们维护的这些长度的value是随长度变大递增的!!!(这个可以证明!),于是就可以用二分来找能接在当前的那个人后面的最长的长度。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 8 const int maxn = 1e5 + 10; 9 10 struct Node { 11 int x, y,id; 12 bool operator < (const Node& tmp) const { 13 return x < tmp.x||x==tmp.x&&y>tmp.y; 14 } 15 }arr[maxn]; 16 17 int n,maxlen; 18 int pre[maxn],dp[maxn],yy[maxn]; 19 20 void init() { 21 memset(pre, -1, sizeof(pre)); 22 maxlen = 0; 23 } 24 25 int main() { 26 while (scanf("%d", &n) == 1 && n) { 27 init(); 28 for (int i = 0; i < n; i++) { 29 scanf("%d%d", &arr[i].x, &arr[i].y); 30 arr[i].id = i + 1; 31 } 32 sort(arr, arr + n); 33 dp[0] = 1; yy[++maxlen] = 0; 34 for (int i = 1; i < n; i++) { 35 int low = 1, hig = maxlen+1; 36 if (arr[i].y <= arr[yy[low]].y) { 37 yy[low] = i; 38 continue; 39 } 40 while (low + 1 < hig) { 41 int mid = low + (hig - low) / 2; 42 if (arr[yy[mid]].y < arr[i].y) low = mid; 43 else hig = mid; 44 } 45 pre[i] = yy[low]; 46 int len = low + 1; 47 if (len > maxlen) { 48 yy[++maxlen] = i; 49 } 50 else { 51 if (arr[i].y < arr[yy[len]].y) yy[len] = i; 52 } 53 } 54 vector<int> ans; 55 int p = yy[maxlen]; 56 while (p != -1) { 57 ans.push_back(p); 58 p = pre[p]; 59 } 60 printf("%d\n", ans.size()); 61 for (int i = 0; i < ans.size() - 1; i++) printf("%d ", arr[ans[i]].id); 62 printf("%d\n", arr[ans[ans.size() - 1]].id); 63 } 64 return 0; 65 }
还有一种复杂的解决方法,用二分查找符合区间,然后用线段树查区间最大值。
待续。。。