网络流24题-3

来源:互联网 发布:石家庄广电网络 编辑:程序博客网 时间:2024/06/15 01:35

最小路径覆盖问题

«问题描述:
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个
顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶
点开始,长度也是任意的,特别地,可以为 0。G 的最小路径覆盖是 G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图 G 的最小路径覆盖。
提示:设 V={1,2,1⁄4 ,n},构造网络 G1=(V1,E1)如下:
V1 = { x 0 , x 1 ,  , x n } È { y 0 , y 1 ,  , y n } ,
E1 = { ( x 0 , x i ) : i Î V } È { ( y i , y 0 ) : i Î V } È { ( x i , y j ) : ( i . j ) Î E }
每条边的容量均为 1。求网络 G1 的( x 0 , y 0 )最大流。
«编程任务:
对于给定的给定有向无环图 G,编程找出 G 的一个最小路径覆盖。
«数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 n 和 m。n 是给定有向无环图
G 的顶点数, m 是 G 的边数。 接下来的 m 行,每行有 2 个正整数 i 和 j, 表示一条有向边(i,j)。
«结果输出:
程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出
一条路径。文件的最后一行是最少路径数。
输入文件示例
input.txt
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
输出文件示例
output.txt
1 4 7 10 11
2 5 8
3 6 9
3
【问题分析】

有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决。

【建模方法】

构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi。对于原图中存在的每条边(i,j),在二分图中连接边(Xi,Yj)。然后把二分图最大匹配
模型转化为网络流模型,求网络最大流。

最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数。沿着匹配边查找,就是一个路径上的点,输出所有路径即可。

【建模分析】

对于一个路径覆盖,有如下性质:

1、每个顶点属于且只属于一个路径。
2、路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。

所以我们可以把每个顶点理解成两个顶点,一个是出发,一个是目标,建立二分图模型。该二分图的任何一个匹配方案,都对应了一个路径覆盖方案。如果匹配数为0,那么显然路径数=顶点数。每增加一条匹配边,那么路径覆盖数就减少一个,所以路径数=顶点数 - 匹配数。要想使路径数最少,则应最大化匹配数,所以要求二分图的最大匹配。

注意,此建模方法求最小路径覆盖仅适用于有向无环图,如果有环或是无向图,那么有可能求出的一些环覆盖,而不是路径覆盖。
—byvoid
个人理解:拆点的思想很重要,拆点把模型转化成了二分图,而且为路径连续提供了基础,如果匹配意味着点不会重复,所以是每个顶点只属于一个路径。

#include<iostream>#include<math.h>#include<cstring>#include<algorithm>#include<vector>#include<queue>using namespace std;const int maxn=10000;const int INF=0x7fffffff;struct edge{    int to,cap,rev;};vector<edge> G[maxn];int iter[maxn];int level[maxn];void add_edge(int from,int to,int cap){    G[from].push_back((edge){to,cap,G[to].size()});    G[to].push_back((edge){from,0,G[from].size()-1});}void bfs(int s){    memset(level,-1,sizeof(level));    level[s]=0;    queue<int> que;    que.push(s);    while(!que.empty())    {        int v=que.front();        que.pop();        for(int i=0;i<G[v].size();i++)        {            edge &e=G[v][i];            if(e.cap>0&&level[e.to]<0)            {                level[e.to]=level[v]+1;                que.push(e.to);            }        }    }}int dfs(int v,int t,int f){    if(v==t)        return f;    for(int i=iter[v];i<G[v].size();i++)    {        edge &e=G[v][i];        if(e.cap>0&&level[v]<level[e.to])        {            int d=dfs(e.to,t,min(e.cap,f));            if(d>0)            {                e.cap-=d;                G[e.to][e.rev].cap+=d;                return d;            }        }    }    return 0;}int max_flow(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)            return flow;        memset(iter,0,sizeof(iter));        int f;        while((f=dfs(s,t,INF))>0)            flow+=f;    }}int main(){    int n,m,x,y;    cin>>n>>m;    int s=0,t=2*n+1;    for(int i=0;i<m;i++)    {        cin>>x>>y;        add_edge(x,y+n,1);    }    for(int i=1;i<=n;i++)    {        add_edge(s,i,1);        add_edge(i+n,t,1);    }    int ans=max_flow(s,t);    cout<<n-ans<<endl;    return 0;}

下面是带路径回溯的

#include<iostream>#include<math.h>#include<cstring>#include<algorithm>#include<vector>#include<queue>#include<cstdio>using namespace std;const int maxn=10000;const int INF=0x7fffffff;struct edge{    int to,cap,rev;};vector<edge> G[maxn];int iter[maxn];int level[maxn];int match[maxn],path[maxn];bool vis[maxn];void add_edge(int from,int to,int cap){    G[from].push_back((edge){to,cap,G[to].size()});    G[to].push_back((edge){from,0,G[from].size()-1});}void bfs(int s){    memset(level,-1,sizeof(level));    level[s]=0;    queue<int> que;    que.push(s);    while(!que.empty())    {        int v=que.front();        que.pop();        for(int i=0;i<G[v].size();i++)        {            edge &e=G[v][i];            if(e.cap>0&&level[e.to]<0)            {                level[e.to]=level[v]+1;                que.push(e.to);            }        }    }}int dfs(int v,int t,int f){    if(v==t)        return f;    for(int i=iter[v];i<G[v].size();i++)    {        edge &e=G[v][i];        if(e.cap>0&&level[v]<level[e.to])        {            int d=dfs(e.to,t,min(e.cap,f));            if(d>0)            {                e.cap-=d;                G[e.to][e.rev].cap+=d;                return d;            }        }    }    return 0;}int max_flow(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)            return flow;        memset(iter,0,sizeof(iter));        int f;        while((f=dfs(s,t,INF))>0)            flow+=f;    }}int main(){    int n,m,x,y;    cin>>n>>m;    int s=0,t=2*n+1;    for(int i=0;i<m;i++)    {        cin>>x>>y;        add_edge(x,y+n,1);    }    for(int i=1;i<=n;i++)    {        add_edge(s,i,1);        add_edge(i+n,t,1);    }    int ans=max_flow(s,t);    int j,p;    for(int v=2*n;v>n;v--)    {        for(j=0;j<G[v].size();j++)        {            edge &e=G[v][j];            if(e.cap==1)              {                    match[e.to]=v-n;                    break;              }        }    }        for(int i=1;i<=n;i++)        {            if(vis[i])                continue;            p=0;            for(j=i;j;j=match[j])            {                path[++p]=j;                vis[j]=true;            }            for (j=1;j<p;j++)                {                    cout<<path[j]<<" ";                }            cout<<path[j]<<endl;        }    cout<<n-ans<<endl;    return 0;}
原创粉丝点击