洛谷 P2764 LibreOJ 6002 最小路径覆盖问题

Posted wawcac

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 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("\n");
        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",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("\n");
        }
    }
    printf("%d\n",n-ans);//最小路径覆盖=总点数-最大匹配 
    return 0;
}

 

以上是关于洛谷 P2764 LibreOJ 6002 最小路径覆盖问题的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ 6002 最小路径覆盖(最大流)

洛谷 P2764 解题报告

洛谷 [P2764]最小路径覆盖问题

洛谷 P2764 最小路径覆盖问题匈牙利算法

洛谷 P1683 入门

LibreOJ #119. 最短路