目錄
1,題目描述
Sample Input:
6 8
1 2
1 3
5 2
5 4
2 3
2 6
3 4
6 4
5
1 5 2 3 6 4
5 1 2 6 3 4
5 1 2 3 6 4
5 2 1 6 3 4
1 2 3 4 5 6
Sample Output:
3 4
題目大意
給出一個有向圖,判斷給出的序列是否爲拓撲序列。
知識補充——拓撲排序
(以下內容來自百度百科,晴神筆記)
問題的引入——AOV網
一個較大的工程往往被劃分成許多子工程,我們把這些子工程稱作活動(activity)。在整個工程中,有些子工程(活動)必須在其它有關子工程完成之後才能開始,也就是說,一個子工程的開始是以它的所有前序子工程的結束爲先決條件的,但有些子工程沒有先決條件,可以安排在任何時間開始。爲了形象地反映出整個工程中各個子工程(活動)之間的先後關係,可用一個有向圖來表示,圖中的頂點代表活動(子工程),圖中的有向邊代表活動的先後關係,即有向邊的起點的活動是終點活動的前序活動,只有當起點活動完成之後,其終點活動才能進行。通常,我們把這種頂點表示活動、邊表示活動間先後關係的有向圖稱做頂點活動網(Activity On Vertex network),簡稱AOV網。
拓撲排序
拓撲排序是將有向無環圖G的所有頂點排成一個線性序列,使得對圖G中的任意兩個頂點u、v,如果存在邊u->v,那麼在序列中u- -定在v前面。這個序列又被稱爲拓撲序列。
算法實現
- 定義一個隊列Q,並把所有入度爲0的結點加入隊列。
- 取隊首結點,輸出。然後刪去所有從它出發的邊,並令這些邊到達的頂點的入度減1,如果某個頂點的入度減爲0,則將其加入隊列。
- 反覆進行2操作,直到隊列爲空。如果隊列爲空時入過隊的結點數目恰好爲N,說明拓撲排序成功,圖G爲有向無環圖;否則,拓撲排序失敗,圖G中有環。
可使用鄰接表實現拓撲排序。顯然,由於需要記錄結點的入度,因此需要額外建立一一個數組inDegree[MAXV],並在程序一開始讀 入圖時就記錄好每個結點的入度。
2,思路
題目比較簡單,只是判斷序列是否爲拓撲序列即可。
1,構建圖,並統計每個節點的入度;
2,針對給定的序列,依次判斷一個點,若當前點入度爲0,則將其所有的鄰接點入讀減一,繼續判斷下一個;否則,即可判定不是拓撲序列;
3,AC代碼
#include<bits/stdc++.h>
using namespace std;
int N, M, K, inDegree[1005], inTemp[1005]; //Nd頂點數 M有向邊數 K查詢數目
vector<int> ans, graph[1005];
bool judge(int temp[]){ // 判斷是否爲拓撲序列 true是 false不是
memcpy(inTemp, inDegree, (N+1) * sizeof(int)); // !!!節點編號從1開始 所以是N+1
for(int i = 0; i < N; i++){
if(inTemp[temp[i]] == 0){
for(auto it : graph[temp[i]])
inTemp[it]--; // 該節點temp[i]對應的所有相鄰節點入度減一
}else return false;
}
return true;
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
scanf("%d%d", &N, &M);
int a, b;
for(int i = 0; i < M; i++){
scanf("%d%d", &a, &b);
graph[a].push_back(b);
inDegree[b]++; // 有向邊指向的頂點入度加一
}
scanf("%d", &K);
int temp[N];
for(int i = 0; i < K; i++){
for(int j = 0; j < N; j++) // 獲取拓撲序列
scanf("%d", &temp[j]);
if(!judge(temp))
ans.push_back(i);
}
for(int i = 0; i < ans.size(); i++)
printf("%d%c", ans[i], i == ans.size()-1 ? '\n':' ');
return 0;
}
4,解題過程
一發入魂o(* ̄▽ ̄*)ブ