最小路径覆盖问题[网络流24题之3]
2016-05-19 15:41
417 查看
问题描述:
给定有向图 G=(V,E) 。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖。 P 中路径可以从 V 的任何一个顶点开始,长度也是任意的,特别地,可以为 0 。 G 的最小路径覆盖是 G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图 G 的最小路径覆盖。
提示:设 V= { 1,2,...,n },构造网络 G1=(V1,E1) 如下:
V1= { x0,x1,...,xn } ∪ { y0,y1,...,yn };
E1= { (x0,xi):i∈V } ∪ { (yi,y0):i∈V } ∪ { (xi,yj):(i.j)∈E }
每条边的容量均为 1 。求网络 G1 的 (x0,y0) 最大流。
编程任务:
对于给定的给定有向无环图 G ,编程找出 G 的一个最小路径覆盖。数据输入:
文件第 1 行有 2 个正整数 n 和 m 。 n 是给定有向无环图 G 的顶点数, m 是 G 的边数。接下来的 m 行,每行有 2 个正整数 i 和 j ,表示一条有向边 (i,j) 。结果输出:
从第 1 行开始,每行输出一条路径。最后一行是最少路径数。输入文件示例 :
11 121 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
输出文件示例:
1 4 7 10 112 5 8
3 6 9
3
分析:
因为根据题意我们知道每一个点只能走一遍,所以我们可以把一个点 x ,拆成两个点 x0 和 x1 ,让 x0 来连接所有从其他定点引向 x 的边,让 x1 来连接所有由 x 引出的边,把所有边连起来就是一个经典的二分图匹配问题了,以下是建立图 N 的过程1 :将所有的顶点 x 拆成两个点 x0 和 x1
2 :对于题目输入的边 (x,y),连接一条容量为 1,从 x1 到 y0 的有向边
3 :新增一个源 S ,连接一条容量为 1,从 S 到 i0(1<=i<=n) 的有向边
4 :新增一个汇 T ,连接一条容量为 1,从 i1(1<=i<=n) 到 T 的有向边
求图 N 的最大流,记为 r ,则最少路径数为 n−r ,最后的输出路径是比较麻烦的(这一部分我也打得很丑,因为不想放弃我的 head,nxt,to,tot 之类的命名,所以我打了一个 namespace ),我就不多说了。
/* 附一个网络流 24 题不需要输出路径之类的网站 http://oi.nks.edu.cn/ */
代码
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; namespace Cai{ int tot = 0; int head[347]={0},nxt[347],to[347]; void add(int a,int b){ ++tot;nxt[tot]=head[a];head[a]=tot;to[tot]=b; } void print(){ int i = 1; while(head[i]){ for(int j=head[i];j;j=nxt[j]) printf("%d ",to[j]); i++; putchar(10); } } int get_count(int a){ for(int i=1;;i++) if(to[head[i]] == a) return head[i]; } }; const int inf = 0x3f3f3f3f; int n,m; int head[347],nxt[13347],to[13347],wei[13347]; int que[347]; int c[347]; bool mark[347]; int ans; int tot = 1; int read(); void add(int,int); bool bfs(); bool dinic(int); void found(int&,int); int main(){ n=read();m=read(); for(int i=n;i>=1;--i){ add(1,i<<1); add((i<<1)+1,(n+1)<<1); } for(int i=1,a,b;i<=m;++i){ a=read();b=read(); add(a<<1,(b<<1)+1); } while(bfs()) while(dinic(1)) ; int count1 = 0; int count2 = 0; for(int i=head[1];i;i=nxt[i]) if(!wei[i] && !mark[to[i]]){ ++count2; count1=count2; found(count1,to[i]); Cai::add(count1,to[i]>>1); } Cai::print(); printf("%d",ans); return 0; } int read(){ int in = 0; char ch = getchar(); while(ch>'9' || ch<'0') ch = getchar(); while(ch<='9'&&ch>='0'){ in = in*10+ch-'0'; ch = getchar(); } return in; } void add(int from,int tp){ ++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=1; ++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0; } bool bfs(){ int now; int H=0,T=1; memset(c,0,sizeof c); que[1] = 1; c[1] = 1; do{ now = que[++H]; for(int i=head[now];i;i=nxt[i]) if(!c[to[i]] && wei[i]){ c[to[i]] = c[now]+1; que[++T] = to[i]; } }while(H<T); return c[(n+1)<<1]; } bool dinic(int place){ if(place == (n+1)<<1) return true; for(int i=head[place];i;i=nxt[i]) if(c[to[i]]==c[place]+1 && wei[i]) if(dinic(to[i])){ --wei[i]; ++wei[i^1]; return true; } return false; } void found(int& count,int place){ if(mark[place]) return; mark[place] = true; for(int i=head[place];i;i=nxt[i]) if(!wei[i]){ if(!mark[to[i]^1]){ if(!(to[i]^1)){ ++ans; return; } found(count,to[i]^1); Cai::add(count,(to[i]^1)>>1); } else{ count = Cai::get_count((to[i]^1)>>1); return; } } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性