hiho 第118周 网络流四·最小路径覆盖
描述
国庆期间正是旅游和游玩的高峰期。
小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周 网络流四·最小路径覆盖的更多相关文章
- hihocoder #1394 : 网络流四·最小路径覆盖(最小路径覆盖)
#1394 : 网络流四·最小路径覆盖 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 国庆期间正是旅游和游玩的高峰期. 小Hi和小Ho的学习小组为了研究课题,决定趁此机 ...
- hihoCoder 1394 : 网络流四·最小路径覆盖
题目链接:https://hihocoder.com/problemset/problem/1394 题目说是网络流,但是其实就是求有向无环图的最小路径覆盖. 不会网络流,只好用二分匹配了. 把每个点 ...
- ●hihocoder #1394 网络流四·最小路径覆盖
题链: http://hihocoder.com/problemset/problem/1394 题解: 有向图最小路径覆盖:最少的路径条数不重不漏的覆盖所有点. 注意到在任意一个最小路径覆盖的方案下 ...
- [HihoCoder1394]网络流四·最小路径覆盖
题目大意:从有向无环图中选出若干点不想交的链,使得这些链覆盖所有的点,并且链的条数最小. 思路:设超级源点$S$.超级汇点$T$.将$N$个点复制一份,分为$A$部和$B$部.对于$A$部的所有点$A ...
- hihoCoder 网络流四·最小路径覆盖
题面带解释 hihoCoder感觉很好. 网络流的精华就是建图 #include<cstdio> #include<iostream> #include<algorith ...
- 网络流二十四题之P2764 最小路径覆盖问题
题目描述 给定有向图 G=(V,E)G=(V,E) .设 PP 是 GG 的一个简单路(顶点不相交)的集合.如果 VV 中每个定点恰好在PP的一条路上,则称 PP 是 GG 的一个路径覆盖.PP中路径 ...
- 【网络流24题】 No.3 最小路径覆盖问题 (网络流|匈牙利算法 ->最大二分匹配)
[题意] 给定有向图 G=(V,E).设 P 是 G 的一个简单路(顶点不相交) 的集合.如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖. P 中路径可以从 V 的任何一 ...
- LOJ6002 - 「网络流 24 题」最小路径覆盖
原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...
- LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖
6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...
随机推荐
- SQL order 排序
select * from emp;
- 查看Linux服务器各种信息方法
有的时候需要搜集服务器的各种信息,比如cpu信息,内存信息,linux版本信息,安装的各种软件信息等等.下面总结几种主要指标的查看方法. 1. 查看Linux发行版信息 [root@pcmweb ~] ...
- 反射认识_03_改变成员变量Fields
包01:package ReflectionChange; public class ReflectionPoint_AB { String str1="access"; Stri ...
- SQL Server练习
SQL Server 基本语法: http://www.w3school.com.cn/sql/sql_intro.asp 练习1: 运行语句: USE [Test1] select FNumber, ...
- linux抓包方法
tcpdump -i eth0 -X -w data.cap 得到的包保存到本地wireshark解析ip.addr == url && http.request.url contai ...
- app_field.clear_dependent_fields
可以调用APP_FIELD.clear_dependent_fields和APP_FIELD.set_dependent_field来将两个(或多个)Item建立关联,当一个为空时,另一个不可录入,反 ...
- c语言 typedef
在C和C++编程语言中,typedef是一个关键字.它用来对一个资料类型取一个新名字.目的是为了使源代码更易于阅读和理解. 来看以下程式码: int coxes; int jaffa; ... c ...
- mysql去除重复数据
select可以取别名,delete不能. 1.使用mysql进行delete from操作时,若子查询的 FROM 字句和更新/删除对象使用同一张表,会出现错误. mysql> DELETE ...
- Objective-C语言控制语句
• 分支语句• 循环语句• 跳转语句 Objective-C中的控制语句有以下几类:• 分支语句:if-else, switch• 循环语句:while, do-while, for• 与程序转移有关 ...
- Oracle读书笔记
数据区(也叫数据扩展区)由一组连续的Oracle块所构成的Oracle存储结构,一个或多个数据块组成一个数据区,一个或多个数据区再组成一个断(Segment). 数据块是Oracle逻辑存储中的最小的 ...