zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    BT协议分析(1)—1.0协议
    Qt线程(2) QThread中使用WorkObject
    新浪微博的开放平台官方文档太粗略,记:仿大平台来实现
    58同城 骗子太多
    代码实现业务经验(程序员的核心能力)
    gitbash 本地文件提交为一个新的项目 到 gitlab
    Spring 核心容器 IOC
    spring AOP 理解
    java不返回某些字段,包括 null
    CentOS7安装 Redis5 单实例
  • 原文地址:https://www.cnblogs.com/fightfordream/p/6523984.html
Copyright © 2011-2022 走看看