最小路徑覆蓋問題(網絡流24題)


洛谷鏈接:最小路徑覆蓋問題

題目描述

«問題描述:

給定有向圖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 )最大流。

«編程任務:

對於給定的給定有向無環圖G,編程找出G的一個最小路徑覆蓋。

輸入輸出格式

輸入格式:

 

件第1 行有2個正整數n和m。n是給定有向無環圖G 的頂點數,m是G 的邊數。接下來的m行,每行有2 個正整數i和j,表示一條有向邊(i,j)。

 

輸出格式:

 

從第1 行開始,每行輸出一條路徑。文件的最后一行是最少路徑數。

 

輸入輸出樣例

輸入樣例#1: 
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
輸出樣例#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 }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM