«問題描述:
給定有向圖G=(V,E)。設P 是G 的一個簡單路(頂點不相交)的集合。如果V 中每個頂點恰好在P 的一條路上,則稱P是G 的一個路徑覆蓋。P 中路徑可以從V 的任何一個頂點開始,長度也是任意的,特別地,可以爲0。G 的最小路徑覆蓋是G 的所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個有向無環圖G 的最小路徑覆蓋。
關於最小不相交路徑覆蓋:
最小路徑覆蓋(path covering):是“路徑” 覆蓋“點”,即用盡量少的不相交簡單路徑覆蓋有向無環圖G的所有頂點,即每個頂點嚴格屬於一條路徑。路徑的長度可能爲0(單個點)。
最小路徑覆蓋數=G的點數-最小路徑覆蓋中的邊數。應該使得最小路徑覆蓋中的邊數儘量多,但是又不能讓兩條邊在同一個頂點相交。拆點:將每一個頂點i拆成兩個頂點Xi和Yi。然後根據原圖中邊的信息,從X部往Y部引邊。所有邊的方向都是由X部到Y部。因此,所轉化出的二分圖的最大匹配數則是原圖G中最小路徑覆蓋上的邊數。因此由最小路徑覆蓋數=原圖G的頂點數-二分圖的最大匹配數便可以得解。
關於最小可相交路徑覆蓋:
算法:先用floyd求出原圖的傳遞閉包,即如果a到b有路徑,那麼就加邊a->b。然後就轉化成了最小不相交路徑覆蓋問題。
證明:爲了連通兩個點,某條路徑可能經過其它路徑的中間點。比如1->3->4,2->4->5。但是如果兩個點a和b是連通的,只不過中間需要經過其它的點,那麼可以在這兩個點之間加邊,那麼a就可以直達b,不必經過中點的,那麼就轉化成了最小不相交路徑覆蓋。
參考神牛博客點擊打開鏈接
對於存儲路徑可以存下路徑最末尾的點,然後向上輸出
#include<bits/stdc++.h>
using namespace std;
const int inf=1000000000;
const int maxn=20000,maxm=1e6+10;
struct Edge{
int v,f,nxt;
};
int src,sink;
int g[maxn+10];
int nume;
Edge e[maxm*2+10];
void addedge(int u,int v,int c) {
e[++nume].v=v;
e[nume].f=c;
e[nume].nxt=g[u];
g[u]=nume;
e[++nume].v=u;
e[nume].f=0;
e[nume].nxt=g[v];
g[v]=nume;
}
void init() {
memset(g,0,sizeof(g));
nume=1;
}
queue<int> que;
bool vis[maxn+10];
int dist[maxn+10];
int n,m,k,dfn;
int h[maxn];
int a[maxn][30];
bool bfs() {
memset(dist,0,sizeof(dist));
while(!que.empty())que.pop();
vis[src]=true;
que.push(src);
while(!que.empty()) {
int u=que.front();
que.pop();
for(int i=g[u];i;i=e[i].nxt) {
if(e[i].f && !vis[e[i].v]) {
que.push(e[i].v);
dist[e[i].v]=dist[u]+1;
vis[e[i].v]=true;
}
}
}
return vis[sink];
}
int dfs(int u,int delta) {
if(u==sink) {
return delta;
}
else {
int ret=0;
for(int i=g[u];delta&&i;i=e[i].nxt) {
if(e[i].f && dist[e[i].v]==dist[u]+1) {
int dd=dfs(e[i].v,min(e[i].f,delta));
e[i].f-=dd;
e[i^1].f+=dd;
delta-=dd;
ret+=dd;
}
}
return ret;
}
}
int maxflow() {
int ret=0;
while(true) {
memset(vis,0,sizeof(vis));
bfs();
if(!vis[sink]) return ret;
ret+=dfs(src,inf);
}
}
int to[maxn];
int in[maxn];
int main() {
init();
scanf("%d%d",&n,&m);
src=0;
sink=n+n+1;
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,n+v,1);
}
for(int i=1;i<=n;i++) {
addedge(src,i,1);
addedge(n+i,sink,1);
}
int ans=n-maxflow();
for(int u=1;u<=n;u++) {
for(int i=g[u];i;i=e[i].nxt) {
int v=e[i].v;
if(e[i].f==0) {
to[u]=v-n;
in[v-n]++;
}
}
}
for(int i=1;i<=n;i++) {
if(!in[i]) {
int u=i;
while(u>0) {
printf("%d ",u);
u=to[u];
}
puts("");
}
}
printf("%d\n",ans);
}