1. 程式人生 > >Minimal Labels CodeForces - 825E (反向拓撲排序+優先佇列)

Minimal Labels CodeForces - 825E (反向拓撲排序+優先佇列)

傳送門

題意:有一個有向無環圖,然後要給每個結點附上標籤,如果從v到u有一條邊,那麼v的標籤小於u的標籤,那麼v的標籤要小於u的標籤,最後使得從頂點1到頂點n的字典序最小,輸出這個字典序

題解:這題一眼看上去用拓撲排序,然後拍了個正向拓撲排序+每次找出最小的進入佇列中進行查詢,wa6,仔細分析一波,如果圖示這樣的

那麼按照正向拓撲的方法下來排序是1423,實際上答案是1342,這就得考慮一個問題就是真正的讓標號小的標籤也小,那麼應該怎麼做呢?使用反向拓撲排序,使用大根堆進行入隊,然後從後往前賦值,就完美的解決了這個問題。

附上程式碼:

#include<bits/stdc++.h>

using namespace std;

const int maxn=1e5+50;

int cnt[maxn],used[maxn],ans[maxn];

struct edge{
    int u,v,next;
}edges[maxn];
int head[maxn],tot;

void add_edge(int a,int b)
{
    edges[tot].u=a;
    edges[tot].v=b;
    edges[tot].next=head[a];
    head[a]=tot++;
}

int main()
{
    int n,m;
    memset(head,-1,sizeof(head));
    tot=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add_edge(v,u);
        cnt[u]++;
    }
    priority_queue<int>q;
    for(int i=1;i<=n;i++){
        if(cnt[i]==0){
            q.push(i);
        }
    }
    int temp=n;
    while(!q.empty()){
        int u=q.top();
        ans[u]=temp--;
        used[u]=true;
        q.pop();
        for(int i=head[u];i!=-1;i=edges[i].next){
            int to=edges[i].v;
            if(used[to]==true){
                continue;
            }
            cnt[to]--;
            if(cnt[to]==0){
                q.push(to);
            }
        }
    }
    printf("%d",ans[1]);
    for(int i=2;i<=n;i++){
        printf(" %d",ans[i]);
    }
    return 0;
}