bzoj2109 [Noi2010]Plane 航空管制 贪心 拓补排序

本文介绍了一种航班调度算法,通过连接反向边处理起飞顺序限制,实现拓扑排序找到每架飞机最小起飞序号。使用桶和队列优化处理最晚起飞时间和相对起飞顺序限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:定义一个航班的起 飞序号为该航班在起飞序列中的位置,即是第几个起飞的航班。 起飞序列还存在两类限制条件:  第一类(最晚起飞时间限制):编号为 i的航班起飞序号不得超过 ki;  第二类(相对起飞顺序限制):存在一些相对起飞顺序限制(a, b),表示 航班 a的起飞时间必须早于航班 b。求每个飞机在可行的起飞序列中最小的那个数。

拓补很显然,但是直接连边比较难以处理第二种限制,于是我们显然想到连反边。
考虑连反边以后,那么就可以直接做拓补序,对于每一个数,我们在topsort的时候,尽量走完他后面的点,对于x,考虑他在拓补序列中的下一个数v,如果当前时间>v的最晚限制,那么很明显不能走v,于是我们用个桶存起来,等到当前时间<=v的最早时刻(其实就是能放则放)再把他重新加入队列中。
然后假如当前要求y的答案,那么我们对于全图topsort的时候不要加入y,保证把y后面的点走完以后再加入y,至于时间限制就如上处理,最后队列为空时的时间就是y起飞的最小时间。
时间复杂度应该是On2logn

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
int n,m;
queue<int>q;
int head[N],next[N],go[N],d[N],dd[N],lim[N],tot;
int tim[2005][2005];
inline void add(int x,int y)
{
    go[++tot]=y;
    next[tot]=head[x];
    head[x]=tot;
}
inline int topsort(int u)
{
    fo(i,0,n)tim[i][0]=0;
    fo(i,1,n)d[i]=dd[i];
    while (!q.empty())q.pop();
    fo(i,1,n)
    if (!d[i]&&i!=u)
    {
        if (lim[i]==n)q.push(i);
        else tim[lim[i]][++tim[lim[i]][0]]=i;
    }
    int Time=n;
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=head[x];i;i=next[i])
        {
            int v=go[i];
            d[v]--;
            if (v!=u&&!d[v])
            {
                if (lim[v]>=Time)q.push(v);
                else tim[lim[v]][++tim[lim[v]][0]]=v;
            }
        }
        Time--; 
        fo(i,1,tim[Time][0])q.push(tim[Time][i]);
        tim[Time][0]=0; 
    }
    return Time;
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&lim[i]),lim[i]=lim[i]>n?n:lim[i];
    fo(i,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(y,x),d[x]++;
    }
    fo(i,1,n)dd[i]=d[i];
    fo(i,1,n)printf("%d ",topsort(i));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值