国庆期间正是旅游和游玩的高峰期。
小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:这我也知道!实现嘛,交给我吧!
第1行:2个整数N,M。1≤N≤500,0≤M≤20,000。
第2..M+1行:2个数字u,v,表示一条有向边(u,v)。保证不会出现重复的边,且不存在环。
第1行:1个整数,表示最少需要的调查团数量。
7 7 1 2 1 3 2 4 3 4 4 5 4 6 5 7
3
最小路径覆盖 + 最大匹配 = n
然后网络流即可-.-
代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
struct node{
int to,w,vap;
}Q;
vector<node>V[1205];
bool fafe[1205];
void add_edge(int a,int b,int c)
{
Q.to=b;Q.w=c;Q.vap=V[b].size();
V[a].push_back(Q);
Q.to=a;Q.w=0;Q.vap=V[a].size()-1;
V[b].push_back(Q);
}
int ex_dfs(int x,int tt)
{
fafe[x]=true;
if (x==tt) return 1;
for (int i=0;i<V[x].size();i++)
{
int v=V[x][i].to;
if (fafe[v]||V[x][i].w==0) continue;
int k=ex_dfs(v,tt);
if (k)
{
V[x][i].w--;
V[v][V[x][i].vap].w++;
return 1;
}
}
return 0;
}
int main()
{
int n,m,a,b;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add_edge(a,b+n,1);
}
for (int i=1;i<=n;i++)
{
add_edge(0,i,1);
add_edge(i+n,2*n+1,1);
}
a=1;b=0;
while (a)
{
a=0;
memset(fafe,false,sizeof(fafe));
a=ex_dfs(0,2*n+1);
b+=a;
}
printf("%d\n",n-b);
return 0;
}
文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态
文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境
文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn
文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker
文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机
文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk
文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入
文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。 Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。
文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动
文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计
文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;gt;Jni-&amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图
文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法