«問題描述:算法
給定有向圖G=(V,E)。設P 是G 的一個簡單路(頂點不相交)的集合。若是V 中每一個頂點剛好在P 的一條路上,則稱P是G 的一個路徑覆蓋。P 中路徑能夠從V 的任何一個頂點開始,長度也是任意的,特別地,能夠爲0。G 的最小路徑覆蓋是G 的所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個有向無環圖G 的最小路徑覆蓋。編程
提示:設V={1,2,.... ,n},構造網絡G1=(V1,E1)以下:網絡
每條邊的容量均爲1。求網絡G1的( 0 x , 0 y )最大流。spa
«編程任務:設計
對於給定的給定有向無環圖G,編程找出G的一個最小路徑覆蓋。rest
輸入格式:code
件第1 行有2個正整數n和m。n是給定有向無環圖G 的頂點數,m是G 的邊數。接下來的m行,每行有2 個正整數i和j,表示一條有向邊(i,j)。blog
輸出格式:ci
從第1 行開始,每行輸出一條路徑。文件的最後一行是最少路徑數。
1 4 7 10 11 2 5 8 3 6 9 3
1<=n<=150,1<=m<=6000
可能不少人看到這個提示反而更懵逼了。先解釋一下題意,大概就是給你有向圖,樣例是一張這樣的圖:
而後求出的最小路徑數就是3了,三條路徑也很容易能夠看出。
咱們先考慮一下如何求出二分圖的最小路徑覆蓋。
這裏介紹一個定理:
最小路徑覆蓋數=|G|-二分圖最大匹配數(|G|是有向圖中的總邊數)
爲何是這樣的呢?首先咱們知道,在二分圖中一個點就表明者一條路徑。那麼若是此時二分圖內沒有連邊,這個公式是成立的。每當二分圖內增長一條邊,最大匹配數就會+1,而一條匹配邊會鏈接二分圖中的兩個點,那麼兩個點間原本有兩條路徑覆蓋,就變成了一條。同理,每加入一條邊匹配數就會+1,路徑覆蓋數就會-1。因此這個公式是成立的。可是這個公式是對二分圖適用的,如何將它轉化到這個問題上來呢?
這時咱們先將一個點拆A成出點Ax和入點Ay,那麼在連有向邊A -> B的時候就將Ax連向By。就將有向圖變成了一個二分圖。咱們看一下樣例轉化爲二分圖的樣子(n == 11)。
那麼這樣咱們只須要求出將每一個點拆點組成二分圖後的最大匹配就能夠了。
最後在輸出路徑的時候就記錄下每一個入點的連入邊,而後用並查集記錄一下一條路徑的起點就行了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=15000+5; 4 const int inf=2147483647; 5 6 int n, m, s, t; 7 int cnt = 1; 8 int ans = 0; 9 int last[N*10]; 10 int lev[N*10]; 11 int fa[N*10]; 12 13 struct edge{ 14 int to, next, cap, from; 15 }e[N*10]; 16 17 void add(int x,int y,int z){ 18 e[++cnt].to = y; 19 e[cnt].from = x; 20 e[cnt].cap = z; 21 e[cnt].next = last[x]; 22 last[x] = cnt; 23 } 24 25 bool bfs(){ 26 memset(lev,-1,sizeof(lev)); 27 queue <int> q; lev[s] = 0; q.push(s); 28 while(!q.empty()){ 29 int x = q.front(); q.pop(); 30 for(int i=last[x];i;i=e[i].next){ 31 int to = e[i].to; 32 if(e[i].cap && lev[to] == -1) 33 lev[to] = lev[x]+1 , q.push(to); 34 } 35 } 36 return lev[t] != -1; 37 } 38 39 int dfs(int x,int flow){ 40 if(x == t) return flow; 41 int rest = 0; 42 for(int i=last[x];i;i=e[i].next){ 43 int to = e[i].to; 44 if(lev[to] == lev[x]+1 && e[i].cap){ 45 int f = dfs(to , min(flow-rest,e[i].cap)); 46 if(f){ 47 rest += f; 48 e[i].cap -= f; 49 e[i^1].cap += f; 50 } 51 } 52 } 53 return rest; 54 } 55 56 int find(int x){ 57 if(fa[x] == x) return x; 58 return fa[x] = find(fa[x]); 59 } 60 61 void output(int x){ 62 cout << x << ' '; 63 for(int i=last[x];i;i=e[i].next) 64 if(e[i].cap == 0 && e[i].to > n) 65 output(e[i].to-n); 66 } 67 68 int main(){ 69 int x, y; cin >> n >> m; 70 s = 0 , t = n*2+1; 71 for(int i=1;i<=m;i++){ 72 cin >> x >> y; 73 add(x,y+n,1); add(y+n,x,0); 74 } 75 for(int i=1;i<=n;i++) add(s,i,1),add(i,s,0); 76 for(int i=n+1;i<=n*2;i++) add(i,t,1),add(t,i,0); 77 while(bfs()) ans += dfs(s,inf); 78 for(int i=1;i<=n;i++) fa[i] = i; 79 for(int i=2;i<=cnt;i++){ 80 if(e[i].cap == 0 && e[i].to>n && e[i].to<=n*2 && e[i].from>0 && e[i].from<=n) 81 if(fa[e[i].from] != fa[e[i].to-n]) 82 fa[e[i].to-n] = fa[e[i].from]; 83 } 84 for(int i=1;i<=n;i++) 85 if(fa[i] == i) output(i), cout << endl; 86 cout << n-ans << endl; 87 return 0; 88 }