洛谷 P2764 解題報告
P2764 最小路徑覆蓋問題
問題描述:
給定有向圖\(G=(V,E)\)。設\(P\) 是\(G\) 的一個簡單路(頂點不相交)的集合。如果\(V\) 中每個頂點恰好在\(P\) 的一條路上,則稱\(P\)是\(G\) 的一個路徑覆蓋。\(P\) 中路徑可以從\(V\) 的任何一個頂點開始,長度也是任意的,特別地,可以為\(0\)。\(G\) 的最小路徑覆蓋是\(G\)的所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個有向無環圖\(G\) 的最小路徑覆蓋。
提示:設\(V={1,2,.... ,n}\),構造網絡\(G_1=(V_1,E_1)\)
如下:
\(V_1=\{x_1,x_2,...,x_n\}\cup\{y_1,y_2,...y_n\}\)
\(E_1=\{(x_0,x_i):i\in V\}\cup\{(y_0,y_i):i\in E \}\cup\{(x_i,y_j):(i,j)\in E\}\)
即每條邊的容量均為1。求網絡\(G_1\)最大流。
輸入輸出格式
輸入格式:
件第1 行有2個正整數\(n\)和\(m\)。\(n\)是給定有向無環圖\(G\) 的頂點數,\(m\)是\(G\) 的邊數。接下來的\(m\)行,每行有\(2\) 個正整數\(i\)和\(j\),表示一條有向邊\((i,j)\)。
輸出格式:
從第1 行開始,每行輸出一條路徑。文件的最後一行是最少路徑數。
說明
\(1<=n<=150,1<=m<=6000\)
由@zhouyonglong提供SPJ
其實提示說的很清楚了。
這裏用我自己的感性語言解釋一下。
描述:將圖中每個點拆成兩個,分成兩個圖。把連原來的邊連上。跑二分圖匹配,最小路徑數=總點數-最大匹配數。
解釋:
不難發現,路徑數+路徑集合中邊的數量=總點數。(肽鏈)
總點數不變,我們就可以轉化到求最大的邊的數量。
而對於原圖中的每一個點\(i\),都可以分成以下四中情況。
為了使邊的數量盡量大,我們應該多令情況(3)出現。
而這幾種情況中又一個點最多戳某一個點屁股,也只能被最多被一個戳。
那麽,劈配? 匹配?
再看看我們跑的二分圖是什麽,是不是很明了~
CODE:
#include <cstdio> #include <cstring> const int N=160; int n,m; struct edge { int to,next; }g[N*N]; int head[N],cnt=0; void add(int u,int v) { g[++cnt].to=v; g[cnt].next=head[u]; head[u]=cnt; } int used[N],match[N]; bool m_find(int u) { for(int i=head[u];i;i=g[i].next) { int v=g[i].to; if(!used[v]) { used[v]=1; if(!match[v]||m_find(match[v])) { match[v]=u; return true; } } } return false; } void dfs(int now) { if(!match[now]) {printf("%d ",now);return;} dfs(match[now]); printf("%d ",now); } int main() { scanf("%d%d",&n,&m); int u,v; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); } int ans=0; for(int i=1;i<=n;i++) { memset(used,0,sizeof(used)); if(m_find(i)) ans++; } memset(used,0,sizeof(used)); for(int i=1;i<=n;i++) used[match[i]]=1; for(int i=1;i<=n;i++) if(!used[i]) {dfs(i);printf("\n");} printf("%d\n",n-ans); return 0; }
2018.5.6
洛谷 P2764 解題報告