UVA - 10305 --- 拓撲排序

題目鏈接:https://vjudge.net/problem/UVA-10305#author=acmparand

題目:就是裸的輸出拓撲序列

算法介紹:

幾個術語:(參考《大話數據結構 第七章 圖 7.8 拓撲排序)

AOV網(Activity On Vertex Network):用一個圖表示一個工程前後執行順序(又如:各個課程之間的選修次序),用頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖即爲AOV網

拓撲序列:設G=(V, E)是一個具有n個頂點的有向圖,V中的頂點序列v1、v2,.....,vn;滿足若從頂點vi到vj有一條路徑,則在頂點序列中頂點vi必在頂點vj之前,這樣的序列就是一個拓撲序列。

(上面的定義,emmm,很正式,其實直觀上理解,就是你找出一個序列,你按照這個序列進行工作或者學習,不存在障礙,拿課程選修來說,就是你找出一個序列,你按照這個序列順序學習,不會遇到一門課,你還沒學它的先修課就開始學它了,那樣你肯定學的一臉懵逼是吧~~)理解之後,你會發現,拓撲排序在現實工程中還是很有用的,可以避免很多等待的時間成本,提高效率。

拓撲排序:其實就是對一個有向圖構造拓撲序列的過程。

其中,如果構造完畢後,發現還存在圖中的頂點不在這個序列中(哪怕只有一個),那麼可以肯定,這個網存在環(迴路),不是AOV網。

算法思想:

1.  從圖中選擇一個入度爲0的頂點輸出到拓撲序列

2. 從圖中刪除此頂點,並刪除以此頂點的弧

3. 重複1、2操作,直到輸出圖中所有頂點或者AOV網不存在入度爲0的頂點

代碼:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define random(a,b)    a+rand()%(b-a+1)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 1e3 + 50;
const int INFint = 0xffffff;
const ll INFll = (ll)1e18;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline int readint(){
    int sgn = 1; int sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

inline ll readll(){
    ll sgn = 1; ll sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

int n, m;
int cnt[maxn];      //每個頂點的入度
vector<int> v[maxn];    //鄰接表


void init(){
    memset(cnt, 0, sizeof(cnt));
    for(int i = 0; i < maxn; ++i){
        v[i].clear();
    }
}

void solve(){
    while(scanf("%d%d", &n, &m) == 2){
        if(!n && !m)    break;
        init();
        for(int i = 0; i < m; ++i){
            int u = readint();      // 邊的起點
            int w = readint();      // 邊頂點終點
            v[u].push_back(w);      // 建邊
            cnt[w]++;               // 終點的入度加1
        }
        vector<int> ans;            // 記錄彈出棧的數字,即最終的拓撲序列
        stack<int> st;              // 存放入度爲0的頂點
        for(int i = 1; i <= n; ++i){    // 初始時,遍歷放入入度爲0的頂點
            if(cnt[i] == 0)
                st.push(i);
        }
        while(!st.empty()){         // 直到棧爲空
            int now = st.top();     // 當前的頂點
            st.pop();               // 從圖中刪除該頂點 需要更新與此頂點相連的頂點的入度
            ans.push_back(now);
            // 遍歷該頂點的所有邊的另一端頂點, 將另一端的頂點的入度減1
            int Size = v[now].size();
            for(int i = 0; i < Size; ++i){
                int tmp = v[now][i];
                cnt[tmp]--;
                if(cnt[tmp] == 0)   // 當頂點的入度爲0時,入棧
                    st.push(tmp);
            }
        }
        int Size = ans.size();      // 輸出該拓撲序列
        for(int i = 0; i < Size; ++i){
            printf("%d", ans[i]);
            if(i != Size - 1)   printf(" ");
        }
        printf("\n");
    }
}


int main()
{
    solve();
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章