【题解】HNOI-2015 菜肴制作

最近要写题解时才发现自己之前写过,风格囧人,不要介意(。・∀・)ノ゙

Problem

bzoj_die

题目概要:
给定一张图,求字典序最靠前的拓扑序,若无则输出Impossible!

Solution

这题是给定一张图,再求字典序最靠前的拓扑序,一开始有一点点思路就是先往①所在分支走,强行取①,再往②走,强行取②,我甚至为我的暴力制定了宏伟的蓝图:

tarjan+dp+dijkstra t a r j a n 判 环 + 树 状 d p 求 当 前 点 可 遍 历 到 的 最 小 点 + 类 似 于 d i j k s t r a 之 类 的 东 东
后来一看,发现若是一个点有俩爹则会挂 (╯°Д°)╯︵ ┻━┻

冷静冷静,发现题目重在优先取小数字,但是会有一些大数字放在小叔子前边儿挡着小数字,看不到啊 (ʘдʘ╬)

那我们就从后边儿看,优先取大数字,从拓扑图(事实证明有可能是假的拓扑图)的后面,也就是叶子结点开始 ,就能巧妙地绕过障碍数字

(o)o[BINGO!] ( o ゜ ▽ ゜ ) o ☆ [ B I N G O ! ]
%%%%%再拜ysp大佬%%%%%

所以每次取走叶子结点中最大的,就有可能有结点变成叶子结点

明眼人一看就知道要用堆
每次取最大的叶子加入ans数组,删掉,再把这个叶子的爹爹们儿子数-1,当有爹爹没儿子时,把这个没儿子的爹爹加入堆中,最后逆向输出ans数组

Code

#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))

template <typename _Tp> inline void read(_Tp &x){
    char c11=getchar();x=0;bool booo=0;
    while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
    if(booo)x=-x;
    return ;
}

const int maxn=100050,maxm=100050;
int n,m;
bool bo[maxn];
struct node {int next,v;} a[maxm];
int head[maxn],p=0;
int tot[maxn],s[maxn];
int heap[maxn],top=0;

inline void add(int,int);
void init();
inline void push(int x){          //强烈建议手打堆,好调试,无bug,时间复杂度还小
    heap[++top]=x;
    int pp=top;
    while(pp>1&&heap[pp>>1]<heap[pp]){swap(heap[pp>>1],heap[pp]);pp>>=1;}
    return ;
}

inline void pop(){
    heap[1]=heap[top--];
    int pp=1;
    while(((pp<<1)<=top&&heap[pp<<1]>heap[pp])||((pp<<1|1)<=top&&heap[pp<<1|1]>heap[pp])){
        pp<<=1;
        if(heap[pp]<heap[pp|1])pp|=1;
        swap(heap[pp],heap[pp>>1]);
    }
    return ;
}

void work(){
    for(int i=1;i<=n;i++)
        if(!tot[i])
            push(i);
    if(!top){
        printf("Impossible!\n");
        return ;
    }
    for(int i=n;i;i--){
        if(!top){
            printf("Impossible!\n");
            return ;
        }
        int x=heap[1];pop();
        s[i]=x;
        for(int j=head[x];j;j=a[j].next){
            int v=a[j].v;
            tot[v]--;
            if(!tot[v])push(v);
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",s[i]);
    printf("\n");
    return ;
}

int main(){
    int T;
    read(T);
    while(T--){
        init();
        work();
    }
    return 0;
}

void init(){
    top=0;p=0;
    cl(bo);cl(head);cl(tot);
    read(n);read(m);
    int A,B;
    for(int i=1;i<=m;i++){
        read(A);read(B);
        add(B,A);
    }
}

inline void add(int u,int v){
    a[++p].v=v;
    a[p].next=head[u];
    head[u]=p;
    tot[v]++;
    return ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值