Codeforces Gym101246H:``North-East''(LIS+思维)
http://codeforces.com/gym/101246/problem/H
题意:在二维平面上有n个点,从最左下角的点出发,每次走只能走在当前的点的右上角的点(xj > xi, yj > yi)。问在走了最长路径的前提下有哪些点是可能被走到的,哪些点是必须被走到的。
思路:比赛的时候以为是图论的题目,结果可以转化为LIS求解,太强啦。
首先直接对坐标按照x从小到大进行排序,如果x相同,y从大到小排序。为什么y是从大到小排序呢?因为在后面我们只看y的大小来做LIS,即通过排序消掉了x维,那么如果x相同的话,y小的在前面会使长度变大,但是此时的x是相同的,并不满足题意要严格在右上角的要求,如果y大的在前面就不会使得长度变长了。因此是从大到小排序。
做LIS,是为了得到一个数组:h[i]代表i这个点的LIS的长度是多少。这样就可以直接求解了。观察下面这样一个样例。
(每次画图都是画得很恶心)
我们知道,假设要从3走到4,那么3的y必须严格小于后面的4的y的,否则这个点是不可能走的。
这样我们看图中,7、8两点的4肯定是由6号点的3贡献的,因为4、5号点的3的y是大于等于后面所有的4的。
这样我们可以逆向思维,从后往前扫,对于每个LIS的长度维护一个最大的y值,用一个maxh[]数组表示。设求得LIS最长长度为len,长度为len的所有点都可能走到,当扫到长度为len-1的时候,只有yi是小于maxh[len]才有可能走到,就这样扫到1。
还要解决哪些点必须走到,其实就是在可能走到的点里面,长度为h的点有且仅有一个的时候,这个点就必须走到,因为如果不走过这个点,就无法到达下一个长度为h+1的点了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define N 100010 4 struct node { 5 int x, y, id; 6 } p[N]; 7 int n, len, h[N], maxh[N], dp[N], Hash[N]; 8 vector<int> maybe, must; 9 10 bool cmp(const node &a, const node &b) { if(a.x == b.x) return a.y > b.y; return a.x < b.x; } 11 12 bool cp(const int &a, const int &b) { return p[a].id < p[b].id; } 13 14 void LIS() { // 求解LIS 15 sort(p + 1, p + 1 + n, cmp); 16 dp[1] = p[1].y; h[1] = len = 1; 17 for(int i = 2; i <= n; i++) { 18 if(p[i].y > dp[len]) { dp[++len] = p[i].y; h[i] = len; } 19 int tmp = lower_bound(dp + 1, dp + 1 + len, p[i].y) - dp; 20 dp[tmp] = p[i].y; h[i] = tmp; 21 } 22 } 23 24 void solve() { 25 // 处理出哪些是可能的 26 for(int i = 1; i <= n; i++) maxh[i] = -100000000; 27 for(int i = n; i >= 1; i--) 28 if(h[i] == len) { maxh[h[i]] = max(maxh[h[i]], p[i].y); maybe.push_back(i); } 29 else if(maxh[h[i]+1] > p[i].y) { maxh[h[i]] = max(maxh[h[i]], p[i].y); maybe.push_back(i); } 30 // 处理出哪些是必须的 31 for(int i = 0; i < maybe.size(); i++) Hash[h[maybe[i]]]++; 32 for(int i = 0; i < maybe.size(); i++) 33 if(Hash[h[maybe[i]]] == 1) must.push_back(maybe[i]); 34 35 sort(maybe.begin(), maybe.end(), cp); 36 sort(must.begin(), must.end(), cp); 37 printf("%d ", maybe.size()); 38 for(int i = 0; i < maybe.size(); i++) printf("%d ", p[maybe[i]].id); puts(""); 39 printf("%d ", must.size()); 40 for(int i = 0; i < must.size(); i++) printf("%d ", p[must[i]].id); puts(""); 41 } 42 43 int main() { 44 freopen("input.txt", "r", stdin); 45 freopen("output.txt", "w", stdout); 46 scanf("%d", &n); 47 for(int i = 1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y), p[i].id = i; 48 LIS(); 49 solve(); 50 return 0; 51 }