描述

国庆期间正是旅游和游玩的高峰期。

小Hi和小Ho的学习小组为了研究课题,决定趁此机会派出若干个调查团去沿途查看一下H市内各个景点的游客情况。

H市一共有N个旅游景点(编号1..N),由M条单向游览路线连接。在一个景点游览完后,可以顺着游览线路前往下一个景点。

为了避免游客重复游览同一个景点,游览线路保证是没有环路的。

每一个调查团可以从任意一个景点出发,沿着计划好的游览线路依次调查,到达终点后再返回。每个景点只会有一个调查团经过,不会重复调查。

举个例子:

上图中一共派出了3个调查团:

1. 蓝色:调查景点;2

2. 橙色:调查景点;1->3->4->6

3. 绿色:调查景点;5->7

当然对于这个图还有其他的规划方式,但是最少也需要3个调查团。

由于小组内的人数有限,所以大家希望调查团的数量尽可能少,同时也要将所有的景点都进行调查。

当然,如何规划调查团线路的任务落到了小Hi和小Ho的头上。

提示:最小路径覆盖

小Ho:所以这一次我们应该如何来解决这个问题呢?

小Hi:嗯,这次的问题被称为最小路径覆盖。给定一个有向无环图,用最少的路径数量去保证所有点都被覆盖住。

小Ho:既然有名字,那一定有固定的解法了?

小Hi:没错,最小路径覆盖的结果等于N-最大二分匹配。

小Ho:二分匹配?这和二分匹配有什么关系?给定的有向图不一定是二分图吧。

小Hi:当然不是在原图上进行的二分匹配了。我们需要对原图进行转化,同时这一次我们还要学习如何用网络流去解决二分匹配的问题。

小Ho:好,你快给我讲讲。

小Hi:好的好的,你别急。我们先从例子来分析:

在这个例子中,我们选择的三条路径都被染上了颜色。你有发现什么特殊之处么?

小Ho:嗯...<小Ho思考了一小会儿>...并没有什么特别的地方啊?

小Hi:把黑色的边去掉,你再看看呢?主要注意的是每个点的出入度数量。

小Ho:对于一条路径,起点的入度为0,终点的出度为0,中间节点的出入度都为1。但这不是路径都应该具有的性质么?

小Hi:这个性质就是我们解决题目的关键!

每一个点最多只能有1个后继,同时每一个点最多只能有1个前驱。

假如我们选择了一条边(u,v),也就等价于把前驱u和后继v匹配上了。这样前驱u和后继v就不能和其他节点匹配。

小Ho:那就是一个前驱匹配一个后继?

小Hi:是的,利用这个我们可以这样来构图:

将每一个点拆分成2个,分别表示它作为前驱节点和后继节点。将所有的前驱节点作为A部,所有后继节点作为B部。

接下来进行连边,若原图中存在一条边(u,v),则连接A部的u和B部的v。

那么小Ho,你在这个上面做一个最大二分匹配怎么样?

小Ho:好!......完成了。

小Hi:不错,让我再把对应的颜色染出来:

其中实线表示被选中的匹配,虚线表示未被选中的。

有没有发现,和原图刚好有着对应的关系。未被选中的匹配也正好对应了原图中我们没有选择的黑色边。

小Ho:是的呢?这是为什么呢?

小Hi:其实原理很简单。我们进行的匹配是前驱和后继的匹配。假如存在选中的匹配(i,j)和(j,k)。则表示原图中存在一条路径(i,j,k)。

比如例子中的匹配(1,3),(3,4),(4,6)就对应了原图中的路径(1,3,4,6)。

这样在匹配结束的时候,我们就可以直接通过匹配的情况来确定选中的路径。

小Ho:这个我懂了,但是如何保证这样就能得到最小的路径覆盖呢?

小Hi:你想想,每一条路径起点有什么特殊的地方?

小Ho:路径的起点入度为0...哦!我知道了。

如果一个点是路径起点的话,它在B部的节点一定是没有匹配上的。

经过最大匹配算法后,B部剩下没有被匹配的点一定是最少的,也就对应了最小需要的路径数。

所以最小路径覆盖的结果才是N-最大匹配数。

小Hi:正是这样,这样问题也就解决了。接下来第二个问题,怎么用网络流来解决二分匹配呢?

小Ho:上一次我们讲了二分多重匹配,二分匹配不就是它的简化版么。

只需要把源点s到A部的边和B部到汇点t的边容量限定为1就可以了!

小Hi:嗯,那么就只差最后一步了。

小Ho:这我也知道!实现嘛,交给我吧!

#include <bits/stdc++.h>
using namespace std; #define maxn 550*2
#define INF 0x3f3f3f3f struct Edge
{
int from,to,cap,flow;
}; struct Dinic
{
int n,m,s,t;
vector<Edge> edge;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn]; void init()
{
for(int i=;i<maxn;i++)
G[i].clear();
edge.clear();
memset(d,,sizeof(d));
memset(vis,,sizeof(vis));
memset(cur,,sizeof(cur));
} void addEdge (int from,int to,int cap)
{
edge.push_back((Edge){from,to,cap,});
edge.push_back((Edge){to,from,,});
m = edge.size();
G[from].push_back(m-);
G[to].push_back(m-);
} bool BFS()
{
memset(vis,,sizeof(vis));
queue<int> Q;
Q.push(s);
d[s] = ;
vis[s] = ;
while(!Q.empty())
{
int x = Q.front();
Q.pop();
for(int i=; i<G[x].size(); i++)
{
Edge & e = edge[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to] = ;
d[e.to] = d[x] + ;
Q.push(e.to);
}
}
}
return vis[t];
} int DFS(int x,int a)
{
if(x==t||a==) return a;
int flow = ,f;
for(int & i = cur[x]; i<G[x].size(); i++)
{
Edge & e = edge[G[x][i]];
if(d[x] + ==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>)
{
e.flow +=f;
edge[G[x][i]^].flow -=f;
flow +=f;
a-=f;
if(a==) break;
}
}
return flow;
} int Maxflow (int s,int t) {
this->s = s;this->t = t;
int flow = ;
while(BFS()) {
memset(cur,,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
} //求最小割S,T;
void new_BFS(int s,int n)
{
memset(vis,,sizeof(vis));
d[s] = ;
vis[s] = ;
queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop(); for(int i=;i<G[u].size();i++)
{
Edge & e = edge[G[u][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to] = ;
d[e.to] = d[u] + ;
Q.push(e.to);
}
}
} int cnt = ;
for(int i=;i<=n;i++)
{
if(vis[i]) cnt++;
}
printf("%d\n",cnt);
for(int i=;i<=n;i++)
if(vis[i]) printf("%d ",i);
puts("");
} }sol; int main()
{ int n,m;
scanf("%d%d",&n,&m);
int s = ,t = *n+;
sol.init();
for(int i=;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
sol.addEdge(u,v+n,);
} for(int i=;i<=n;i++)
sol.addEdge(,i,);
for(int i=n+;i<=*n;i++)
sol.addEdge(i,t,); printf("%d\n",n-sol.Maxflow(s,t)); return ;
}

有向无环图的最小路径覆盖。

建图:

把每个点,拆成入点,和出点,一条路径就是在B块一定是匹配了的,也就是说要求的最小路径覆盖,就是那些没有匹配的点,经过最大匹配后,B块剩下没有匹配的点是最少的,也就对应了最小需要的路径数。

所以: 最小路径覆盖 = N -最大匹配

然后这里求最大匹配,可以直接用匈牙利,也可以网络流。

网络流求最大匹配的建图:

s 到 A块容量为1,A到B有边,容量为1,B块到t容量为1.

hiho 第118周 网络流四·最小路径覆盖的更多相关文章

  1. hihocoder #1394 : 网络流四·最小路径覆盖(最小路径覆盖)

    #1394 : 网络流四·最小路径覆盖 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 国庆期间正是旅游和游玩的高峰期. 小Hi和小Ho的学习小组为了研究课题,决定趁此机 ...

  2. hihoCoder 1394 : 网络流四·最小路径覆盖

    题目链接:https://hihocoder.com/problemset/problem/1394 题目说是网络流,但是其实就是求有向无环图的最小路径覆盖. 不会网络流,只好用二分匹配了. 把每个点 ...

  3. ●hihocoder #1394 网络流四·最小路径覆盖

    题链: http://hihocoder.com/problemset/problem/1394 题解: 有向图最小路径覆盖:最少的路径条数不重不漏的覆盖所有点. 注意到在任意一个最小路径覆盖的方案下 ...

  4. [HihoCoder1394]网络流四·最小路径覆盖

    题目大意:从有向无环图中选出若干点不想交的链,使得这些链覆盖所有的点,并且链的条数最小. 思路:设超级源点$S$.超级汇点$T$.将$N$个点复制一份,分为$A$部和$B$部.对于$A$部的所有点$A ...

  5. hihoCoder 网络流四·最小路径覆盖

    题面带解释 hihoCoder感觉很好. 网络流的精华就是建图 #include<cstdio> #include<iostream> #include<algorith ...

  6. 网络流二十四题之P2764 最小路径覆盖问题

    题目描述 给定有向图 G=(V,E)G=(V,E) .设 PP 是 GG 的一个简单路(顶点不相交)的集合.如果 VV 中每个定点恰好在PP的一条路上,则称 PP 是 GG 的一个路径覆盖.PP中路径 ...

  7. 【网络流24题】 No.3 最小路径覆盖问题 (网络流|匈牙利算法 ->最大二分匹配)

    [题意] 给定有向图 G=(V,E).设 P 是 G 的一个简单路(顶点不相交) 的集合.如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖. P 中路径可以从 V 的任何一 ...

  8. LOJ6002 - 「网络流 24 题」最小路径覆盖

    原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...

  9. LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

    6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

随机推荐

  1. C#面向对象的方法写数组的功能

    上一篇文章用Java方法写出了可以对数组执行的功能,然后在用实例化后的对象调用这些方法来实现这些功能: 这篇随笔改用C#语言实现同样的功能 方法类:Array using System; using ...

  2. break和continue的区别以及标签label的使用

    break表示直接跳出当前循环,break只能运用于switch--case语句以及循环之中 continue则表示跳出当次循环,继续执行下一次循环 label标签则可以选择break,或者conti ...

  3. .net开发之我见,or实现 最简 优化法。knock out type convert 与我之简化orm的实现原理及实现细则,最简化开发法

    现在的.net or构架,大家认同的各种大大小小,ef,subsonic,nhibernate,甚至小一些的petapoco这种,但用过的人我想他们考虑的是比较多. 小一些的Petapoco也有几千行 ...

  4. Java基础(1):Switch语句注意的5个地方

    不得不说的几点小秘密: 1. switch 后面小括号中表达式的值必须是整型或字符型 2. case 后面的值可以是常量数值,如 1.2:也可以是一个常量表达式,如 2+2 :但不能是变量或带有变量的 ...

  5. NVL 和NVL2函数

    NVL 和NVL2函数 NVL函数: nvl(exp1,exp2) -->判断exp1是否是null,如果exp1不是则返回exp1的值,如果exp1为null则返回exp2 nvl2函数: n ...

  6. JSP里比对单选框或复选框的数值而自动打勾

    <table> <tr> <td class="tableleft">状态</td> <td><input typ ...

  7. [MaxOSX] 路由操作

    查看当前路由情况 netstat -nr 添加路由 sudo route -n add x.x.x.x/24 x.x.x.x 可以简单这样子理解: 第1个参数 x.x.x.x/24是远程地址 第2个参 ...

  8. Java 中的System.exit

    在java 中退出程序,经常会使用System.exit(1) 或 System.exit(0). 查看System.exit()方法的源码,如下 /** * Terminates the curre ...

  9. HOWTO Install the MinGW (GCC) Compiler Suite

    Posted July 25th, 2008 by mingwadmin getting started install mingw Automated Installer If you are ne ...

  10. java 网络编程(一)---基础知识和概念了解

    java 为用户提供了十分完善的网络功能: 1. 获取网络上的各种资源(URL) 2. 与服务器建立连接和通信(ServerSocket和Socket) 3. 无连接传递本地数据(DatagramSo ...