您的位置:首页 > 理论基础 > 计算机网络

最小路径覆盖问题[网络流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 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

输出文件示例:

1 4 7 10 11

2 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;
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息