BZOJ2535: [Noi2010]Plane 航空管制2

正解有点巧妙没想出来。学到了,以后这种题,最早呀,最晚呀,拓扑呀,可以往反图方面想想。

参考这位大佬的 https://blog.csdn.net/weixin_34377065/article/details/86020916

应该不少人一开始想的是正着做,弄个优先队列优先让时限早的先飞。这样听起来很对,其实是错的,上面博客有一句话说得很好:因为当前的k小并不意味着后面的点k也小

这里给出一个正着做的错误:3->5  4->2  这里的数字代表时限,箭头代表连边。合法情况应该是4->2->3->5。但是因为优先队列优先时限小的会变成3->4->2->5显然是错误的。因为当前小不代表后面小,当前大也不代表后面大。

那为什么倒着做是正确的呢?因为题目本身给的时限的意思就是最晚飞行时间,那么我们倒着做就是让后面时限大的飞机尽量晚飞,题目保证有解那么肯定不会出现  时限最大的飞机最晚飞  然后无解这种情况。

这是卡着时限飞的意思,能拖就拖。

那么第二问的最早飞行时间,我们枚举飞机i,意思就是飞机i最早只能在这个时间飞,再早了就会影响别人。那么在反图上就是,强制让飞机i不飞(因为在反图的缘故,拖着不飞就是尽量早飞),直到它影响了别人的时刻,这就是它的最早飞行时刻,因为再早了就影响别人了。

这道题还是非常有意思的,值得多多思考。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1e4+10;
int n,m,deg[N],k[N],p[N],rc[N];
vector<int> G[N];

priority_queue<pii> q;
void toposort() {
    int cnt=0;
    for (int i=1;i<=n;i++) 
        if (deg[i]==0) q.push(make_pair(k[i],i));
    while (!q.empty()) {
        pii x=q.top(); q.pop();
        p[++cnt]=x.second;
        for (int i=0;i<G[x.second].size();i++) {
            int y=G[x.second][i];
            if (--deg[y]==0) q.push(make_pair(k[y],y));
        }
    }
    for (int i=cnt;i>=1;i--) printf("%d ",p[i]); puts("");
}

int solve(int t) {
    while (!q.empty()) q.pop();
    for (int i=1;i<=n;i++) deg[i]=rc[i];
    deg[t]=0x3f3f3f3f;  //把t点度数设为无穷大,无法入队 
    for (int i=1;i<=n;i++)
        if (deg[i]==0) q.push(make_pair(k[i],i));
    for (int i=n;i;i--) {
        if (q.empty()) return i;
        pii x=q.top(); q.pop();
        if (x.first<i) return i;
        for (int i=0;i<G[x.second].size();i++) {
            int y=G[x.second][i];
            if (--deg[y]==0) q.push(make_pair(k[y],y));
        }
    }
    return n;
}

int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++) scanf("%d",&k[i]);
    for (int i=1;i<=m;i++) {
        int x,y; scanf("%d%d",&x,&y); 
        G[y].push_back(x); deg[x]++;
    }
    memcpy(rc,deg,sizeof(deg));
    toposort();

    for (int i=1;i<=n;i++) printf("%d ",solve(i));
    return 0;
}
原文地址:https://www.cnblogs.com/clno1/p/10828260.html