网络流与线性规划24题03最小路径覆盖问题

来源:互联网 发布:如何运营淘宝商城 编辑:程序博客网 时间:2024/06/05 20:29

问题描述:
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个
顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶
点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图G 的最小路径覆盖。
编程任务:
对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。
数据输入:
文件input.txt提供输入数据。文件第1 行有2个正整数n和m。n是给定有向无环图
G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。
结果输出:
程序运行结束时,将最小路径覆盖输出到文件output.txt 中。从第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

分析:

最小路径覆盖,还是一窍不通,百度百科了一下最小路径覆盖。

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

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

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

所以最小路径覆盖数=顶点数-最大匹配数。最后输出结果只要沿着匹配边查找即可。

于是这题就转化成了二分图最大匹配问题,二分图又可以用最大流求解。于是目的达到了。

然后今天新学习了一个最短增广路的算法,dinic算法,就是增广层次图和阻塞流来找增广路。用BFS生成增广层次图,用DFS查找阻塞流。

该算法在二分图最大匹配下时间效率可达O(m*n^1/2),而之前的BFS直接查找增广路高达O(n*m^2);这个算法不是最快的,该算法的优点是概念简洁,速度较快。

图的数据结构是邻接表,采用了刘汝佳白书上推荐的STL的vector来表示邻接表,虽然时间效率不是最高的,但是代码很清晰,且容易写,也容易调试。

由于本人比较懒,所以成员函数就写在声明里面了。

最后吐槽一点,还是对二分图理解不深刻,用最大流求解加源点汇点的时候,理解错了,把汇点汇点相应连接的点权值设成无穷,而不是1,导致调试了N个小时,太桑感了。

dinic求二分图最大匹配:

#include<cstdio>#include<vector>#include<iostream>#include<queue>#include<cstring>using namespace std;const int MAX = 1000;const int INF = 10000000;struct Edge{//边结构体    int from,to,cap,flow;Edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d){}};struct dinic{    int n,m,s,t;    vector<int> g[MAX];//邻接表    vector<Edge> edges;//边集    int d[MAX];    int cur[MAX];    bool visit[MAX];    dinic(int nn,int mm)    {        s=0;t=2*nn+1;//源点0,汇点2*n+1        edges.clear();        for(int i=0;i<=nn+1;i++) g[i].clear();        n=nn;m=mm;        int x,y;        //除了源点汇点,把每个点i,分成i和i+n,i是出发点,i+n是目标点        for(int i=0;i<m;i++){            scanf("%d%d",&x,&y);            add(x,y+n,1);        }        for(int i=1;i<=n;i++){            add(s,i,1);//源点到1~n            add(i+n,t,1);//n+1~2*n        }    }    void add(int from,int to,int cap)    {        edges.push_back(Edge(from,to,cap,0));        edges.push_back(Edge(to,from,0,0));        int l=edges.size();        g[from].push_back(l-2);        g[to].push_back(l-1);    }    bool BFS()//bfs增广层次图    {        memset(visit,false,sizeof(visit));        queue<int> q;        q.push(s);        d[s]=0;        visit[s]=true;        while(!q.empty()){            int x=q.front();q.pop();            for(int i=0;i<g[x].size();i++){                Edge &e=edges[g[x][i]];                if(!visit[e.to]&&e.cap>e.flow){                    visit[e.to]=true;                    d[e.to]=d[x]+1;                    q.push(e.to);                }            }        }        return visit[t];    }    int DFS(int x,int a){//dfs求最大流        if(x==t||a==0) return a;        int flow=0,f;        for(int& i=cur[x];i<g[x].size();i++){            Edge &e=edges[g[x][i]];            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){                e.flow+=f;                edges[g[x][i]^1].flow-=f;                flow+=f;                a-=f;                if(a==0) break;            }        }        return flow;    }    int maxflow()//返回最大流    {        int flow=0;        while(BFS()){            memset(cur,0,sizeof(cur));            flow+=DFS(s,INF);        }return flow;    }    void show(int i)//输出路径    {        visit[i]=true;        for(int j=0;j<g[i].size();j++){            Edge &e=edges[g[i][j]];            if(!visit[e.to]&&e.to!=t&&1==e.flow){                printf(" %d",e.to-n);                show(e.to-n);            }        }    }    void print()//输出路径    {int x=maxflow();memset(visit,false,sizeof(visit));        for(int i=1;i<=n;i++){            if(!visit[i]){                printf("%d",i);                show(i);                printf("\n");            }        }        printf("%d\n",n-x);    }};int main(){    int n,m;    scanf("%d%d",&n,&m);    dinic d(n,m);    d.print();    return 0;}