有向无环图的最小路径覆盖。。。。。。题解中给出的讲解很好了。。。无需解释。。。如果是无向图的话,一样建图,最后最小路径覆盖=|V|-match/2。。。。
【问题分析】
有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决。
【建模方法】
构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi。对于原图中存在的每条边
(i,j),在二分图中连接边(Xi,Yj)。然后把二分图最大匹配模型转化为网络流模型,求网络最大流。
最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数。沿着匹配边查找,就是一个路径上的
点,输出所有路径即可。
【建模分析】
对于一个路径覆盖,有如下性质:
1、每个顶点属于且只属于一个路径。
2、路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。
所以我们可以把每个顶点理解成两个顶点,一个是出发,一个是目标,建立二分图模型。该二分图的任
何一个匹配方案,都对应了一个路径覆盖方案。如果匹配数为0,那么显然路径数=顶点数。每增加一条
匹配边,那么路径覆盖数就减少一个,所以路径数=顶点数 - 匹配数。要想使路径数最少,则应最大化
匹配数,所以要求二分图的最大匹配。
注意,此建模方法求最小路径覆盖仅适用于有向无环图,如果有环或是无向图,那么有可能求出的一些
环覆盖,而不是路径覆盖。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define inf 1<<30 #define M 100000 #define N 10000 #define cc(m,v) memset(m,v,sizeof(m)) struct node{ int u,v,f,next; }edge[M]; int head[N],p,cur[N],lev[N]; int que[M],vis[N]; void ainit(){ p=0,cc(head,-1); } bool bfs(int s,int t){ int i,u,v,qin=0,qout=0; cc(lev,-1),lev[s]=0,que[qin++]=s; while(qout!=qin){ u=que[qout++]; for(i=head[u];i!=-1;i=edge[i].next) if(edge[i].f>0 && lev[v=edge[i].v]==-1){ lev[v]=lev[u]+1,que[qin++]=v; if(v==t) return 1; } } return 0; } int dinic(int s,int t){ int i,f,k,u,qin; int flow=0; while(bfs(s,t)){ memcpy(cur,head,sizeof(head)); u=s,qin=0; while(1){ if(u==t){ for(k=0,f=inf;k<qin;k++) if(edge[que[k]].f<f) f=edge[que[i=k]].f; for(k=0;k<qin;k++) edge[que[k]].f-=f,edge[que[k]^1].f+=f; flow+=f,u=edge[que[qin=i]].u; } for(i=cur[u];cur[u]!=-1;i=cur[u]=edge[cur[u]].next) if(edge[i].f>0 && lev[u]+1==lev[edge[i].v]) break; if(cur[u]!=-1) que[qin++]=cur[u],u=edge[cur[u]].v; else{ if(qin==0) break; lev[u]=-1,u=edge[que[--qin]].u; } } } return flow; } void addedge(int u,int v,int f){ edge[p].u=u,edge[p].v=v,edge[p].f=f,edge[p].next=head[u],head[u]=p++; edge[p].u=v,edge[p].v=u,edge[p].f=0,edge[p].next=head[v],head[v]=p++; } void dfs(int k,int n){ vis[k]=1; for(int i=head[k];i!=-1;i=edge[i].next) if(edge[i].f==0 && !vis[edge[i].v] && edge[i].v<=2*n && edge[i].v>=n+1){ printf(" %d",edge[i].v-n); dfs(edge[i].v-n,n); return ; } } int main(){ int n,m,i,ans,u,v; while(scanf("%d%d",&n,&m)!=-1){ ainit(); for(i=1;i<=n;i++) addedge(0,i,1); for(i=1;i<=n;i++) addedge(i+n,2*n+1,1); for(i=1;i<=m;i++){ scanf("%d%d",&u,&v); addedge(u,v+n,1); } ans=n-dinic(0,2*n+1); cc(vis,0); for(i=1;i<=n;i++) if(!vis[i]){ printf("%d",i); dfs(i,n);printf("\n"); } printf("%d\n",ans); } return 0; }