[网络流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的(x0,y0)最大流。

编程任务:

  对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。

数据输入:

  由文件input.txt提供输入数据。文件第1行有2个正整数n和m。n是给定有向无环图G的顶点数,m是G的边数。接下来的m行,每行有2个正整数i 和j,表示一条有向边(i,j)。

结果输出:

  程序运行结束时,将最小路径覆盖输出到文件output.txt中。从第1行开始,每行输出一条路径。文件的最后一行是最少路径数。

题解:

  最小路径覆盖,建图方法:把所有点分别加入二分图中的x集和y集,如果i节点可以连向j,就让在x集中的i连向一条在y集的j,权值为inf。然后让S连向x集,权值为1。让y集连向T,权值为1。跑网络流,路径的个数就是点的总数减去最大流的值。 证明理解

  统计方案也很好理解,正如''证明理解'',里的那样,如果x集里的u流了容量为一的边到y集里的v,则说明v表示的点在DAG中是u表示的点的后继,那么他们两个在同一个路径覆盖上,用并查集把他们联系起来,最后统计一下就成了。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<queue>
  8 #include<vector>
  9 using namespace std;
 10 const int inf=1e9;
 11 const int maxn=1000,maxm=120000;
 12 int N,M,S,T,ANS;
 13 struct Edge{
 14     int to,rest,next;
 15 }e[150000];
 16 int head[maxn],cnt=1;
 17 inline void Addedge(int x,int y,int z){
 18     e[++cnt].to=y; e[cnt].rest=z; e[cnt].next=head[x]; head[x]=cnt;
 19     e[++cnt].to=x; e[cnt].rest=0; e[cnt].next=head[y]; head[y]=cnt;
 20 }
 21 int dis[maxn];
 22 inline bool BFS(){
 23     memset(dis,0,sizeof(dis));
 24     static queue<int> Q;
 25     while(!Q.empty()) Q.pop();
 26     Q.push(S); dis[S]=1;
 27     while(!Q.empty()){
 28         int x=Q.front(); Q.pop();
 29         for(int i=head[x];i;i=e[i].next){
 30             int y=e[i].to;
 31             if(dis[y]==0&&e[i].rest!=0){
 32                 dis[y]=dis[x]+1;
 33                 Q.push(y);
 34             }
 35         }
 36     }
 37     if(dis[T]) return true;
 38     return false;
 39 }
 40 inline int DFS(int x,int flow){
 41     if(x==T) return flow;
 42     int tmp,now=0;
 43     for(int i=head[x];i;i=e[i].next){
 44         int y=e[i].to;
 45         if(dis[y]==dis[x]+1&&e[i].rest!=0){
 46             tmp=DFS(y,min(flow-now,e[i].rest));
 47             e[i].rest-=tmp;
 48             e[i^1].rest+=tmp;
 49             now+=tmp;
 50             if(now==flow) return now;
 51         }
 52     }
 53     if(!now) dis[x]=0;
 54     return now;
 55 }
 56 inline void dinic(){
 57     while(BFS()) ANS-=DFS(S,inf);
 58 }
 59 
 60 int fa[maxn];
 61 inline int find(int x){
 62     if(x!=fa[x]) fa[x]=find(fa[x]);
 63     return fa[x];
 64 }
 65 int main(){
 66     //freopen("path3.in","r",stdin);
 67     //freopen("path3.out","w",stdout);
 68     scanf("%d%d",&N,&M); 
 69     ANS=N; S=0; T=N*2+1;
 70     for(int i=1,u,v;i<=M;i++){
 71         scanf("%d%d",&u,&v);
 72         Addedge(u,v+N,inf);
 73     }
 74     for(int i=1;i<=N;i++){
 75         Addedge(S,i,1);
 76         Addedge(i+N,T,1);
 77     }
 78     dinic();
 79     for(int i=1;i<=N;i++) fa[i]=i;
 80     for(int i=1;i<=N;i++){
 81         for(int j=head[i];j;j=e[j].next){
 82             int y=e[j].to;
 83             if(e[j].rest==inf-1){
 84                 int u=i,v=y-N;
 85                 int fau=find(u),fav=find(v);
 86                 if(fau!=fav){
 87                     if(fau<fav) fa[fav]=fau;
 88                     else fa[fau]=fav;
 89                 }
 90             }
 91         }
 92     }
 93     for(int i=1;i<=N;i++){
 94         if(fa[i]==i){
 95             printf("%d ",i);
 96             for(int j=1;j<=N;j++){
 97                 if(j!=i&&fa[j]==i) printf("%d ",j);
 98             }
 99             printf("\n");
100         }
101     }
102     printf("%d\n",ANS);
103     return 0;
104 }

 

转载于:https://www.cnblogs.com/CXCXCXC/p/5199951.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值