zoukankan      html  css  js  c++  java
  • NOI2010 航空管制

    题目

    bzoj上买一 送一

    算法

    知道怎么做之后我对自己很生气,因为这确确实实是一道水题啊!

    题目的意思就是说:给出一个有向图,求它的拓扑序列,每个点有限制(l_u)表示(u)这个点在拓扑序列的位置必须比(l_u)前。

    这个问题实在不好做,因为求拓扑序列的时候不知道放哪个点会比较好,点与点之间是有影响的。vfk告诉我们,可以把问题反过来,然后就一秒钟变水题了。

    问题的反面就是求逆拓扑序,这样的话,(p_u=n - l_u + 1)就表示(u)这个点在逆拓扑序的位置必须比(p_u)后,那当然是越后越好啦,每个点之间就没有影响了。

    我们就可以贪心的取点了。

    无脑的话,我们可以维护一个优先队列,每次取(p_u)最小的点加入到逆拓扑序中。对于第二个子问题,假设我们要让飞机(u)最早起飞,那就是在逆拓扑序中越后越好,这样我们也可以贪心的一直不把(u)放进序列中,直到实在没办法了才放。
    时间复杂度:(O(nm log n))

    优化

    我们不一定要让(p_u)最小的点加入拓扑序,假设现在求拓扑序的第(i)位,我们只要随便让一个(p_u geq i)的点进入就行了,所以我们并不需要用优先队列来求第(i)位的点。用点小技巧优化一下就可以做到(O(nm))了。

    代码

    #include <bits/stdc++.h>
    using namespace std;
     
    const int MAXN = 2003;
    const int MAXM = (int) 1e4 + 3;
     
    int n, m;
    int limit[MAXN];
    pair<int, int> A[MAXN];
     
    struct Edge {
        Edge* next;
        int dest;
    };
     
    Edge memory[MAXM];
    Edge* info[MAXN];
    Edge* curMem;
     
    int que[MAXN];
     
    int solve(int X) {
        static int deg[MAXN];
        fill(deg, deg + n + 1, 0);
        for (int i = 1; i <= n; i ++) 
            for (Edge* pt = info[i]; pt; pt = pt->next)
                deg[pt->dest] ++;
     
        int cur = 1;
        int high = 0;
        for (int i = 1; i <= n; i ++) {
            while (cur <= n && A[cur].first <= i) {
                int u = A[cur].second;
                if (u != X && deg[u] == 0) {
                    que[++ high] = u;
                }
                cur ++;
            }
            if (high < i) return high;
     
            int u = que[i];
            for (Edge* pt = info[u]; pt; pt = pt->next) {
                int v = pt->dest;
                deg[v] --;
                if (deg[v] == 0 && v != X && limit[v] <= i)
                    que[++ high] = v;
            }
        }
        return high;
    }
     
    int main() {
    #ifdef debug
        freopen("input.txt", "r", stdin);
    #endif
     
        scanf("%d%d", &n, &m);
     
        for (int i = 1; i <= n; i ++) {
            scanf("%d", limit + i);
            limit[i] = min(limit[i], n);
            limit[i] = n - limit[i] + 1;
            A[i] = make_pair(limit[i], i);
        }
        sort(A + 1, A + 1 + n);
     
        curMem = memory;
        for (int i = 0; i < m; i ++) {
            int a, b;
            scanf("%d%d", &a, &b);
            curMem->dest = a;
            curMem->next = info[b];
            info[b] = curMem ++;
        }
     
        assert(solve(-1) == n);
        for (int i = n; i > 0; i --)
            printf("%d ", que[i]);
        printf("
    ");
     
        for (int i = 1; i <= n; i ++) {
            printf("%d ", n - solve(i));
        }
        printf("
    ");
     
        return 0;
    }
    
  • 相关阅读:
    SVN自动更新测试服务器工作副本(C#写winform程序实现)
    两台服务器上SQL Server数据库数据互操作示例
    ASP.NET根据URL生成网页缩略图示例程序(C#语言)
    Flutter Widget API
    BPM、BPMN、BPMN2.0概念介绍
    H5多图上传调研
    lIUNX如何加载U盘,光盘
    如何解决exchange2003被中继的问题?(网上转载)
    window无法安装服务
    如何在WORD里面插入不同的页眉和页脚
  • 原文地址:https://www.cnblogs.com/wangck/p/4620231.html
Copyright © 2011-2022 走看看