定義:在一個有向圖中,找出最少的路徑,使得這些路徑通過了全部的點。ios
最小路徑覆蓋分爲最小不相交路徑覆蓋和最小可相交路徑覆蓋。算法
最小不相交路徑覆蓋:每一條路徑通過的頂點各不相同。如圖,其最小路徑覆蓋數爲3。即1->3>4,2,5。閉包
最小可相交路徑覆蓋:每一條路徑通過的頂點能夠相同。若是其最小路徑覆蓋數爲2。即1->3->4,2->3>5。spa
特別的,每一個點本身也能夠稱爲是路徑覆蓋,只不過路徑的長度是0。code
算法:把原圖的每一個點V拆成$V_x$和$V_y$兩個點,若是有一條有向邊A->B,那麼就加邊$A_x->B_y$。這樣就獲得了一個二分圖。那麼最小路徑覆蓋=原圖的結點數-新圖的最大匹配數。blog
證實:一開始每一個點都是獨立的爲一條路徑,總共有n條不相交路徑。咱們每次在二分圖裏找一條匹配邊就至關於把兩條路徑合成了一條路徑,也就至關於路徑數減小了1。因此找到了幾條匹配邊,路徑數就減小了多少。因此有最小路徑覆蓋=原圖的結點數-新圖的最大匹配數。get
由於路徑之間不能有公共點,因此加的邊之間也不能有公共點,這就是匹配的定義。string
習題:POJ1422it
// // main.cpp // POJ1422最小不想交路徑覆蓋 // // Created by beMaster on 16/4/8. // Copyright © 2016年 beMaster. All rights reserved. // #include <iostream> #include <stdio.h> #include <string.h> #include <vector> using namespace std; const int N = 200 + 10; vector<int> g[N]; int cy[N]; bool vis[N]; bool dfs(int u){ for(int i=0; i<g[u].size(); ++i){ int v = g[u][i]; if(vis[v]) continue; vis[v] = true; if(cy[v]==-1 || dfs(cy[v])){ cy[v] = u; return true; } } return false; } int solve(int n){ int ret = 0; memset(cy, -1, sizeof(cy)); for(int i=1;i<=n;++i){ memset(vis, 0, sizeof(vis)); ret += dfs(i); } return n - ret; } int main(int argc, const char * argv[]) { int t,n,m; int u,v; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) g[i].clear(); for(int i=0;i<m;++i){ scanf("%d%d",&u,&v); g[u].push_back(v); } int ans = solve(n); printf("%d\n",ans); } return 0; }
算法:先用floyd求出原圖的傳遞閉包,即若是a到b有路徑,那麼就加邊a->b。而後就轉化成了最小不相交路徑覆蓋問題。io
證實:爲了連通兩個點,某條路徑可能通過其它路徑的中間點。好比1->3->4,2->4->5。可是若是兩個點a和b是連通的,只不過中間須要通過其它的點,那麼能夠在這兩個點之間加邊,那麼a就能夠直達b,沒必要通過中點的,那麼就轉化成了最小不相交路徑覆蓋。
題目:POJ2594
// // main.cpp // POJ2594最小可相交路徑覆蓋 // // Created by beMaster on 16/4/8. // Copyright © 2016年 beMaster. All rights reserved. // #include <iostream> #include <stdio.h> #include <string.h> #include <vector> using namespace std; const int N = 500 + 10; bool dis[N][N]; bool vis[N]; int cy[N]; void floyd(int n){ for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) for(int k=1;k<=n;++k) if(dis[i][k] && dis[k][j])//傳遞可達性 dis[i][j] = true; } bool dfs(int u, int n){ for(int i=1;i<=n;++i){ if(!vis[i] && dis[u][i]){ vis[i] = true; if(cy[i]==-1 || dfs(cy[i], n)){ cy[i] = u; return true; } } } return false; } int solve(int n){ int cnt = 0; memset(cy,-1,sizeof(cy)); for(int i=1;i<=n;++i){ memset(vis,0,sizeof(vis)); cnt += dfs(i, n); } return n - cnt; } int main(int argc, const char * argv[]) { int n,m; int a,b; while(scanf("%d%d",&n,&m),n+m){ for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) dis[i][j] = false; for(int i=1;i<=m;++i){ scanf("%d%d",&a,&b); dis[a][b] = true; } floyd(n); int ans = solve(n); printf("%d\n",ans); } return 0; }
參考: