网络流二十四题之三 —— 最小路径覆盖问题(PATH)


最小路径覆盖问题


Description

给定有向图 G=(V,E)
P G 的一个简单路(顶点不相交)的集合。
如果 V 中每个顶点恰好在 P 的一条路上,则称 P G 的一个路径覆盖。
P 中路径可以从 V 的任何一个顶点开始,长度也是任意的,特别地,可以为 0
G 的最小路径覆盖是 G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图 G 的最小路径覆盖。


Input

1 行有 2 个正整数 n m
n 是给定有向无环图 G 的顶点数, m G 的边数。
接下来的 m 行,每行有 2 个正整数 i j,表示一条有向边 (i,j)


Output

从第 1 行开始,每行输出一条路径。
文件的最后一行是最少路径数。


Sample Input

11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11


Sample Output

1 4 7 10 11
2 5 8
3 6 9
3


Solution

V={12n},构造网络 G1=(V1,E1) 如下:
V1={x0,x1,,xn}{y0,y1,,yn}
E1={(x0,xi):iV}{(yi,y0):iV}{(xi,yj):(i,j)E}
每条边的容量均为 1
求网络 G1 (x0,y0) 最大流即可。


Code

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <queue>  
  5.   
  6. #define Min(x,y) ((x)<(y)?(x):(y))  
  7.   
  8. using namespace std;  
  9.   
  10. const int INF=0x3f3f3f3f;  
  11.   
  12. int m,n,s,t,cnt;  
  13.   
  14. int low[100000],head[100000],nxt[100000],data[100000];  
  15. int dis[1000];  
  16. bool vis[1000];  
  17. queue<int>q;  
  18.   
  19. void add(int x,int y,int z){  
  20.     nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;  
  21.     nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;   
  22. }  
  23.   
  24. bool BFS(){  
  25.     memset(dis,-1,sizeof dis);  
  26.     q.push(s);dis[s]=0;  
  27.     while(!q.empty()){  
  28.         int now=q.front();q.pop();  
  29.         for(int i=head[now];i!=-1;i=nxt[i])  
  30.             if(low[i]&&dis[data[i]]<0){dis[data[i]]=dis[now]+1;q.push(data[i]);}  
  31.     }  
  32.     return dis[t]>0;  
  33. }  
  34.   
  35. int dfs(int now,int flow){  
  36.     if(now==t)return flow;  
  37.     int Flow;  
  38.     for(int i=head[now];i!=-1;i=nxt[i]){  
  39.         if(low[i]&&dis[data[i]]==dis[now]+1){  
  40.             if(Flow=dfs(data[i],Min(flow,low[i]))){  
  41.                 low[i]-=Flow;  
  42.                 low[i^1]+=Flow;  
  43.                 return Flow;  
  44.             }  
  45.         }  
  46.     }  
  47.     return 0;  
  48. }  
  49.   
  50. void dfs2(int now){  
  51.     vis[now]=true;  
  52.     printf(”%d ”,now);  
  53.     for(int i=head[now];i!=-1;i=nxt[i]){  
  54.         if(!vis[data[i]]&&data[i]!=s&&data[i]!=t&&data[i]>n&&data[i]<=n*2&&!low[i])dfs2(data[i]-n);  
  55.     }  
  56. }  
  57.   
  58. int main(){  
  59.     freopen(”path.in”,“r”,stdin);  
  60.     freopen(”path.out”,“w”,stdout);  
  61.     scanf(”%d%d”,&n,&m);  
  62.     memset(head,-1,sizeof head);   
  63.     int weight=n;  
  64.     s=n+n+1,t=n+n+2;  
  65.     for(int i=1;i<=m;i++){  
  66.         int x,y;  
  67.         scanf(”%d%d”,&x,&y);  
  68.         add(x,y+n,1);  
  69.     }  
  70.     for(int i=1;i<=n;i++){add(s,i,1);add(i+n,t,1);}  
  71.     while(BFS()){  
  72.         int flag;  
  73.         while(flag=dfs(s,INF)){  
  74.             weight-=flag;  
  75.         }  
  76.     }  
  77.     for(int i=1;i<=n;i++)if(!vis[i]){  
  78.         dfs2(i);  
  79.         printf(”\n”);  
  80.     }  
  81.     printf(”%d\n”,weight);  
  82.     return 0;  
  83. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值