zoukankan      html  css  js  c++  java
  • 最短路径覆盖问题

    首先诚挚感谢BYvoid大神博客里提供的好题和数据

    此为线性规划与网络流24题的第三题。


    «问题描述:
    给定有向图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 行开始,每行输出
    一条路径。文件的最后一行是最少路径数。

    建模部分分析:

    考虑到在一个满足要求的路径覆盖中:

    每个顶点属于且仅属于一条路径

    每个点处最多出发一条边到达另一顶点

    于是将一个点i拆成两个点Xi和Yi,分别代表从点i出发和到达点i。从而建立一个二分图,若原图中存在边(i,j),则在二分图中构造边(Xi,Yi),容量设为1。

    再建立源点S,汇点T,S与每个X连接一条容量为1的边,T与每个Y连接一条容量为1的边。用最大流求最大二分匹配即可。

    因为每增加一个匹配数,就能多“赚”到一个点。(贪心思想)

    所以二分匹配数最大时,路径数最小。

    最少的路径条数即为点的总数减去最大匹配数。

    #include<cstdio>
    #include<cstring>
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    using namespace std;
    const int MAXN=1010;
    int graph[MAXN][MAXN];
    int match[MAXN];
    int visit[MAXN];
    int path[MAXN];
    int n,m,f=0;
    void init()
    {
        scanf("%d%d",&n,&m);
        int x,y;
        rep(i,1,m)
        {
            scanf("%d%d",&x,&y);
            graph[x][++graph[x][0]]=y;
        }
    }
    bool crosspath(int k)
    {
        rep(i,1,graph[k][0])
        {
            int &t=graph[k][i];                 //试试能不能让k和t配
            if(!visit[t])
            {
                visit[t]=1;                     //标记t在第i轮寻找增广路的时候已经被访问过了
                if(!match[t]||crosspath(match[t]))          //如果t单身或者t的伴可以另外找到一个伴
                {
                    match[t]=k;                              //把t许配给k
                    path[k]=t;
                    return true;
                }
            }
        }
        return false;
    }
    void hungary()
    {
        rep(i,1,n)
        {
            if(crosspath(i)) f++;
            memset(visit,0,sizeof(visit));
        }
    }
    void pout()
    {
        rep(i,1,n)
        {
            int t=i;
            if(!visit[i])
            {
                while(path[t]) t=path[t];
                while(match[t])
                {
                    visit[t]=1;
                    printf("%d ",t);
                    t=match[t];
                }
                printf("%d
    ",t);
            }
        }
        printf("%d",n-f);
    }
    int main()
    {
        freopen("path2.in","r",stdin);
        init();
        hungary();
        pout();
        return 0;
    }
  • 相关阅读:
    C语言 sprintf 函数 C语言零基础入门教程
    C语言 printf 函数 C语言零基础入门教程
    C语言 文件读写 fgets 函数 C语言零基础入门教程
    C语言 文件读写 fputs 函数 C语言零基础入门教程
    C语言 fprintf 函数 C语言零基础入门教程
    C语言 文件读写 fgetc 函数 C语言零基础入门教程
    C语言 文件读写 fputc 函数 C语言零基础入门教程
    C语言 strlen 函数 C语言零基础入门教程
    Brad Abrams关于Naming Conventions的演讲中涉及到的生词集解
    适配器模式
  • 原文地址:https://www.cnblogs.com/zhixingr/p/6993469.html
Copyright © 2011-2022 走看看