zoukankan      html  css  js  c++  java
  • 洛谷 P2764 LibreOJ 6002 最小路径覆盖问题

    题目描述

    «问题描述:

    给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。提示:设V={1,2,.... ,n},构造网络G1=(V1,E1)如下:

    每条边的容量均为1。求网络G1的( 0 x , 0 y )最大流。

    «编程任务:

    对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。

    输入输出格式

    输入格式: 

    件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。

    输出格式:

    从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。

    输入输出样例

    输入样例#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:
    1 4 7 10 11
    2 5 8
    3 6 9
    3

    说明

    1<=n<=150,1<=m<=6000

    吐槽

      这题洛谷居然没有SPJ,大家来这里交吧。要在洛谷上AC就必须像造数据的人那样——用链式前向星反着加边,然后跑dinic。我的匈牙利明明能得出一个最优解,洛谷就是给我爆零,大坏蛋。下面的代码能出解不能在洛谷AC,大家别想复制了。因为一些事,心情不爽,我也不太想在洛谷上交dinic了,抄了个题解混一波通过数。

    解题思路

      裸题。u到v有连边,那么就在二分图里把男u号与女v号牵线,然后跑二分图最大匹配,得到匹配数k,答案最后那行就是$n-k$了。至于前面输出路径,直接顺着二分图走即可,我感觉我的这部分代码挺好懂的(逃)

    我的源代码

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int n,m;
    struct men{
        int w;//对象
        vector<int> lover;
    }man[156];
    int woman[156]={0};
    inline void add(int u,int v)
    {
        man[u].lover.push_back(v);
    }
    
    bool vis[156];
    bool dfs(int u)
    {
        int sz=man[u].lover.size();
        for(int i=0;i<sz;i++)
        {
            int v=man[u].lover[i];
            if(vis[v])continue;
            vis[v]=1;
            if(!woman[v]||dfs(woman[v]))
            {
                woman[v]=u;
                man[u].w=v;
                return 1;
            }
        }
        return 0;
    }
    void print(int i)
    {
        if(i==0||vis[i])
        {
            printf("
    ");
            return;
        }
        vis[i]=1;
        printf("%d ",i);
        print(man[i].w);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        memset(man,0,sizeof(man));
        for(int i=1,u,v;i<=m;i++)
            scanf("%d%d",&u,&v),add(u,v);
        int ans=0;
        for(int i=1;i<=n;i++) reverse(man[i].lover.begin(),man[i].lover.end());
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i))ans++;
        }
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            if(!vis[i]) print(i);
        }
        printf("%d
    ",n-ans);
        return 0;
    }

    洛谷题解代码

    #include <stdio.h>
    #include <cstring>
    #include <queue>
    #define maxn 20000
    #define fill(x,y) memset(x,y,sizeof(x))
    #define min(x,y) x<y?x:y
    #define INF 0x7f7f7f7f
    using namespace std;
    struct edge{int y,w,rev,next;}e[maxn];
    int ls[maxn],n,m,maxE=0,vis[maxn],state[maxn];
    int add(int x,int y,int w)//将边加入
    {
        e[++maxE]=(edge){y,w,maxE+1,ls[x]};
        ls[x]=maxE;
        e[++maxE]=(edge){x,0,maxE-1,ls[y]};
        ls[y]=maxE;
        return 0;
    }
    int bfs(int x)//暴力搜索
    {
        queue <int> t;
        t.push(x);
        fill(state,63);
        state[x]=0;
        while (!t.empty())
        {
            int tt=t.front();t.pop();
            for (int i=ls[tt];i;i=e[i].next)
            {
                if (e[i].w>0&&state[tt]+1<state[e[i].y])
                {
                    state[e[i].y]=state[tt]+1;
                    t.push(e[i].y);
                    if (e[i].y==n+n+1)
                        return true;
                }
            }
        }
        return false;
    }
    int find(int x,int mn)//dinic
    {
        if (x==n+n+1) return mn;
        for (int i=ls[x];i;i=e[i].next)
            if (state[x]+1==state[e[i].y]&&e[i].w>0)
            {
                int d=find(e[i].y,min(mn,e[i].w));
                if (d>0)
                {
                    e[i].w-=d;
                    e[e[i].rev].w+=d;
                    vis[x]=e[i].y;//记录路径
                    return d;
                }
            }
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y+n,1);
        }
        for (int i=1;i<=n;i++)//连边
        {
            add(0,i,1);
            add(n+i,n+n+1,1);
        }
        int ans=0;
    
        while (bfs(0))
        {
            int d=find(0,INF);//最大流
            ans+=d;
        }
    
        for (int i=1;i<=n;i++)
        {
            if (vis[i]!=0)//输出路径
            {
                int t=i;
                do
                {
                    if (t>n) t-=n;
                    printf("%d ",t);
                    int x=vis[t];
                    vis[t]=0;
                    t=x;
                }
                while (t!=0);
                printf("
    ");
            }
        }
        printf("%d
    ",n-ans);//最小路径覆盖=总点数-最大匹配 
        return 0;
    }
  • 相关阅读:
    linux基础指令(下)
    Linux基础命令(中)
    wtforms校验组件
    Linux基础命令(上)
    Scrapy
    SQLAlchemy
    自定义命令
    flask-session
    解决Failed to allocate memory: 8转
    如何做需求
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/7056082.html
Copyright © 2011-2022 走看看