codeforces 4D Mysterious Present

D. Mysterious Present
time limit per test
2 seconds
memory limit per test
64 megabytes
input
standard input
output
standard output

Peter decided to wish happy birthday to his friend from Australia and send him a card. To make his present more mysterious, he decided to make a chain. Chain here is such a sequence of envelopes A = {a1,  a2,  ...,  an}, where the width and the height of the i-th envelope is strictly higher than the width and the height of the (i  -  1)-th envelope respectively. Chain size is the number of envelopes in the chain.

Peter wants to make the chain of the maximum size from the envelopes he has, the chain should be such, that he'll be able to put a card into it. The card fits into the chain if its width and height is lower than the width and the height of the smallest envelope in the chain respectively. It's forbidden to turn the card and the envelopes.

Peter has very many envelopes and very little time, this hard task is entrusted to you.

Input

The first line contains integers nwh (1  ≤ n ≤ 50001 ≤ w,  h  ≤ 106) — amount of envelopes Peter has, the card width and height respectively. Then there follow n lines, each of them contains two integer numbers wi and hi — width and height of the i-th envelope (1 ≤ wi,  hi ≤ 106).

Output

In the first line print the maximum chain size. In the second line print the numbers of the envelopes (separated by space), forming the required chain, starting with the number of the smallest envelope. Remember, please, that the card should fit into the smallest envelope. If the chain of maximum size is not unique, print any of the answers.

If the card does not fit into any of the envelopes, print number 0 in the single line.


题目的意思是有n个二元组(w0, h0), (w1, h1), (w2, h2), (w2, h2) ...

给定一个初始二元组(w, h),再从这n个二元组里面拿出一些来构造成一个二元组序列。

这个序列需要满足的要求是:1.这个序列的w也严格递增,h也严格递增;2.最长。

最后输出这个二元组序列的信息。

方法1. 拓扑排序

  考虑到这些二元组之间存在着偏序关系: e1 < e2 当且仅当 e1.w < e2.w 并且 e1.h < e2.h

  所以可以考虑将这n个二元组建立成一个有向图。存在一条从 e1 到 e2的边 当且仅当 e1 < e2。

  由于这个偏序关系具有传递性且不具有自反性和对称性,故这个有向图是无环的。

  (简单的说就是不存在e1 < e2,e2 < e3,e3 < e1这样的情况)

  那么可以考虑对这个有向无环图进行拓扑排序,在拓扑排序的过程中更新各节点离源点的最大距离。

  最开始建立有向无环图时,需要枚举任意两个点,所以复杂度是O(n^2)

  在拓扑排序的过程中,每个节点只会进入队列一次,每次要扫描从这个点发射出去的边,所以用邻接表实现的话复杂度是O(ne),用邻接矩阵的话复杂度是O(n^2)

  综上,复杂度是O(n^2)

  

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
using namespace std;

struct env {
    int no;
    int w;
    int h;
    int l;
    int deg;
    int prev;
};
env envs[5005];
bool mat[5005][5005];

int cmp(env e1, env e2) {
    if (e1.w < e2.w && e1.h < e2.h) return 1;
    if (e1.w > e2.w && e1.h > e2.h) return -1;
    return 0;
}

int main() {
    int n, w, h;
    int tmpw, tmph;
    int cnt = 0;
    scanf("%d %d %d", &n, &w, &h);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &tmpw, &tmph);
        if (tmpw <= w || tmph <= h) continue;
        envs[cnt].no = i;
        envs[cnt].w = tmpw;
        envs[cnt].h = tmph;
        envs[cnt].l = 1;
        envs[cnt].deg = 0;
        envs[cnt].prev = -1;

        cnt ++;
    }

    queue<int> q;
    for (int i = 0; i < cnt; i++) {
        for (int j = 0; j < cnt; j++) {
            if (cmp(envs[i], envs[j]) > 0) {
                mat[i][j] = true;
                envs[j].deg += 1;
            } else {
                mat[i][j] = false;
            }
        }
        if (envs[i].deg == 0) q.push(i);
    }

    int tmp;
    while (!q.empty()) {
        tmp = q.front();
        for (int i = 0; i < cnt; i++) {
            if (!mat[tmp][i]) continue;

            if (envs[i].l < envs[tmp].l + 1) {
                envs[i].prev = tmp;
                envs[i].l = envs[tmp].l + 1;
            }

            envs[i].deg -= 1;
            if (envs[i].deg == 0) q.push(i);
        }
        q.pop();
    }

    int maxl = -1;
    int pos = -1;
    for (int i = 0; i < cnt; i++) {
        if (maxl < envs[i].l) {
            maxl = envs[i].l;
            pos = i;
        }
    }

    if (maxl == -1) {
        printf("0\n");
    } else {
        stack<int> s;
        while (pos != -1) {
            s.push(pos);
            pos = envs[pos].prev;
        }
        printf("%d\n%d", s.size(), envs[s.top()].no + 1);
        s.pop();
        while (!s.empty()) {
            printf(" %d", envs[s.top()].no + 1);
            s.pop();
        } 
        printf("\n");
    }

    return 0;
}

方法2.图的广度优先遍历

  和方法1一样建立有向无环图.

  然后对图进行广度优先遍历,在遍历过程中不断的更新源点到每个点的最长距离,程序的整体结构和拓扑排序类似,但是广度优先遍历时,一个节点可能多次进入队列,使得使用邻接表和邻接矩阵复杂度分别为O(kne)或者是O(kn^2),其中k是进入队列的次数,在最坏的情况下,k可能会接近n,所以,最终在test case25的时候,超时了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
using namespace std;

struct env {
    int no;
    int w;
    int h;
    int l;
    int prev;
    bool inq;
};
env envs[5005];
bool mat[5005][5005];

int cmp(env e1, env e2) {
    if (e1.w < e2.w && e1.h < e2.h) return 1;
    if (e1.w > e2.w && e1.h > e2.h) return -1;
    return 0;
}

int main() {

    int n, w, h;
    int tmpw, tmph;
    int cnt = 0;
    scanf("%d %d %d", &n, &w, &h);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &tmpw, &tmph);
        if (tmpw <= w || tmph <= h) continue;
        envs[cnt].no = i;
        envs[cnt].w = tmpw;
        envs[cnt].h = tmph;
        envs[cnt].l = 1;
        envs[cnt].prev = -1;
        envs[cnt].inq = false;

        cnt ++;
    }

    for (int i = 0; i < cnt; i++) {
        for (int j = 0; j < cnt; j++) {
            mat[i][j] = (cmp(envs[i], envs[j]) > 0);
        }
    }

    queue<int> q;
    bool prev;
    for (int i = 0; i < cnt; i++) {
        prev = false;
        for (int j = 0; j < cnt; j++) prev = prev || mat[j][i];
        if (prev) continue;
        
        q.push(i);
        envs[i].inq = true;
    }

    int tmp;
    while (!q.empty()) {
        tmp = q.front();
        for (int i = 0; i < cnt; i++) {
            if (!mat[tmp][i]) continue;

            if (envs[i].l < envs[tmp].l + 1) {
                envs[i].prev = tmp;
                envs[i].l = envs[tmp].l + 1;
            }

            if (envs[i].inq) continue;
            q.push(i);
            envs[i].inq = true;

        }
        q.pop();
        envs[tmp].inq = false;
    }

    int maxl = -1;
    int pos = -1;
    for (int i = 0; i < cnt; i++) {
        if (maxl < envs[i].l) {
            maxl = envs[i].l;
            pos = i;
        }
    }

    if (maxl == -1) {
        printf("0\n");
    } else {
        printf("%d\n", envs[pos].l);
        stack<int> s;
        while (envs[pos].prev != -1) {
            s.push(pos);
            pos = envs[pos].prev;
        }
        s.push(pos);
        printf("%d", envs[s.top()].no + 1);
        s.pop();
        while (!s.empty()) {
            printf(" %d", envs[s.top()].no + 1);
            s.pop();
        } 
        printf("\n");
    }

    return 0;
}

方法3.动态规划

  将为二元组定义小于运算符,e1 < e2 当且仅当 e1.w < e2.w 

  按照这个小于运算符的定义对n个二元组排序

  那么最后选出来的最长二元组序列一定是这个有序的二元组序列的子序列。

  所以这时候,问题变成了,求一个序列的最大上升子序列了。常见的动态规划问题。

  

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

struct env {
    int no;
    int w;
    int h;
    int prev;
    int l;
    env(int no, int w, int h, int prev, int l) {
        this->no = no;
        this->w = w;
        this->h = h;
        this->prev = prev;
        this->l = l;
    }

    bool operator < (const env& e) const {
        return (w < e.w);
    }
};

int cmp(env e1, env e2) {
    if (e1.w < e2.w && e1.h < e2.h) return 1;
    return -1;
}

int main() {

    int n, w, h;
    int tmpw, tmph;
    vector<env> envs;
    scanf("%d %d %d", &n, &w, &h);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &tmpw, &tmph);
        if (tmpw <= w || tmph <= h) continue;
        envs.push_back(env(i+1, tmpw, tmph, -1, 1));
    }
    sort(envs.begin(), envs.end());

    for (int i = 0; i < envs.size(); i++) {
        for (int j = 0; j < i; j++) {
            if (cmp(envs[j], envs[i]) <= 0) continue;
            if (envs[i].l >= envs[j].l + 1) continue;
            envs[i].l = envs[j].l + 1;
            envs[i].prev = j;
        }
    }

    int maxl = 0, pos = -1;
    for (int i = 0; i < envs.size(); i++) {
        if (envs[i].l <= maxl) continue;
        maxl = envs[i].l;
        pos = i;
    }

    if (maxl == 0) {
        printf("0\n");
    } else {
        stack<int> s;
        while (pos != -1) {
            s.push(envs[pos].no);
            pos = envs[pos].prev;
        }

        printf("%d\n%d", s.size(), s.top());
        s.pop();
        while (!s.empty()) {
            printf(" %d", s.top());
            s.pop();
        }
        printf("\n");
    }

    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值