码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P2764 最小路径覆盖问题(二分图匹配)

时间:2018-10-02 17:35:23      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:for   文件的   cdn   namespace   problem   最大流   使用   include   有向无环图   

P2764 最小路径覆盖问题

题面

题目描述

?问题描述:

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

技术分享图片

?编程任务:

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

输入输出格式

输入格式:

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

输出格式:

从第 \(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 \leq n \leq 150,1 \leq m \leq 6000\)

思路

“最小路径覆盖大家都会吧?” --老师
“会啊。” --huyufeifei
“嗯。” --logeadd

国庆集训正式开始啦!今天早上讲的是图论。老师打开的 \(PPT\) 标题为 NOI中的图论算法 ,身为蒟蒻的我还以为老师少打了一个 p ,结果讲了一上午的黑题...我的任务计划变成了上午讲过的题:

技术分享图片

讲到P2304 [NOI2015]小园丁与老司机的时候老师问了上面的那个问题,大家都说会,深深感受到了周围都是神仙的恐惧...我就现在把这题写了。

说下思路:首先最小路径覆盖的最坏答案就是 \(n\) ,也就是每个节点都不得不用一条路径去覆盖它。而很容易发现,有边相连的两个点可以用同一路径来覆盖,那么我们就可以把这样的两个点缩到同一条路径之中。能把更多的点缩起来,就能用最少的边达成目的。设我们能缩 \(k\) 组点,那么最终答案就是 \(n-k\)

因为每个点只能缩一次,所以就可以用二分图最大匹配的方法来转换问题,匈牙利算法和最大流都是可行的,在这里我使用的是码量较小的匈牙利。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=160;
const int MAXM=6010;
int n,m,ans,match[MAXN],inv[MAXN];
int cnt,top[MAXN],to[MAXM],nex[MAXM];
int js,edge[MAXN];
bool vis[MAXN];
int read()
{
    int re=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
bool dfs(int now)
{
    for(int i=top[now];i;i=nex[i])
    {
        if(!vis[to[i]])
        {
            vis[to[i]]=true;
            if(!match[to[i]]||dfs(match[to[i]]))
            {
                match[to[i]]=now;
                inv[now]=to[i];
                return true;
            }
        }
    }
    return false;
}
void fd(int now)
{
    vis[now]=true,edge[js++]=now;
    if(inv[now]&&!vis[inv[now]]) fd(inv[now]);
    if(match[now]&&!vis[match[now]]) fd(match[now]);
}
int main()
{
    ans=n=read(),m=read();
    while(m--)
    {
        int x=read(),y=read();
        to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;
    }
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof vis);
        if(dfs(i)) ans--;
    }
    for(int i=1;i<=n;i++)
    {
        js=0;
        if(!vis[i])
        {
            fd(i);
            sort(edge,edge+js);
            for(int j=0;j<js;j++) printf("%d ",edge[j]);
            puts("");
        }
    }
    printf("%d",ans);
    return 0;
}

Luogu P2764 最小路径覆盖问题(二分图匹配)

标签:for   文件的   cdn   namespace   problem   最大流   使用   include   有向无环图   

原文地址:https://www.cnblogs.com/coder-Uranus/p/9736869.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!