2008/2109/2535: [Noi2010]航空管制

题目链接

题目大意:一个有向图,在这个有向图上做拓扑序,并限制某些点i的位置必须在区间 [1,ki] 中,第一问求合法序列,第二问求每个点在拓扑序中最早在什么位置出现。

题解:本来是三倍经验,但是2008没有special judge过不了,只有双倍了23333

考虑第二问:考虑到一下求出全部的答案不太好搞,分开一个个考虑。若直接贪心的话,没法确定先加入哪个入度为0的点(因为有后效性)。把有向图反向,问题就变成了求点最晚的出现位置,没有后效性了

在求点i的答案时,做拓扑序限制点i,剩下的点按限制时间排序,然后一直加入限制时间允许且入度为0的点,直到没法加为止,这时就是i的最晚时间,因为所有能加入的都加入了

对于第一问,可以当作不限制任何点

我的收获:……

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define f first
#define s second
#define M 2005

int n,m,k,t;
int head[M],q[M*10],in[M],r[M],limit[M];

pair <int,int> pla[M];

struct edge{int to,nex;}e[M*10];

void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}

int toposort(int x){
    int h=0,w=0;
    memcpy(r,in,sizeof(in));
    for(int i=1,p=1;i<=n;i++) {
        for (;p<=n&&pla[p].f<i;p++)
            if (!r[pla[p].s]&&pla[p].s!=x)
                q[++w]=pla[p].s;
        if(h<w){
            int u=q[++h];
            for(int j=head[u];j!=-1;j=e[j].nex){
                int v=e[j].to;r[v]--;
                if(!r[v]&&v!=x&&limit[v]<i)
                    q[++w]=v;
            }
        }
        else return w;
    }
    return w;
}

void work()
{
    int tm=toposort(0);
    for(int i=tm;i>=1;i--)
        printf("%d%c",q[i],i>1?' ':'\n');
    for(int i=1;i<=n;i++)
        printf("%d%c",n-toposort(i),i<n?' ':'\n');
}

void init()
{
    int x,y;t=0;memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&pla[i].f),pla[i].f=n-pla[i].f,pla[i].s=i,limit[i]=pla[i].f;
    for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(y,x),in[x]++;
    sort(pla+1,pla+1+n);
}

int main() 
{
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值