【学时·III】 二分图


■基本策略■

其实本质是图论中的网络流

二分图是两个由多个点组成的集合(上部和下部,且没有重叠),两个集合中的点不与该集合内其他的点连通,但和另一个集合内的点连通。我们称这两个集合为上部、下部,或X、Y部,比如:

  • 判定

我们可以通过染色的方法将一个普通的连通图转换为二分图(如果不是连通图,则说明该图存在多个二分图或不为二分图)。由于X部只与Y部相连,Y部也只与X部相连,我们可以把X、Y部染成不同的颜色。通过BFS(DFS也可以)从图里的一个点开始,假设它为X部,则与它相连的点为Y部,之后又为X部……直到访问到一个标记过的点,且该点的标记与将要作的标记不同,则不是二分图。将所有点标记完后还没有冲突,则是二分图。

  • 算法

与二分图相关的有匈牙利算法、König定理,分别处理增广路和最大匹配问题。

  • 最大匹配

二分图中若存在边集 E() 使得其中的边没有交点(共同的顶点),则称 E() 是该二分图的一个匹配。

特别的,若 E() 所含的顶点恰好是二分图中所有的顶点,则称 E() 为完全匹配。

最常考的是最大匹配,此时 E() 所包含的边的数量达到二分图中可能的最大数量。

  • 增广路径

边集 E() 为二分图已经匹配的边,路径P连接不同部的未匹配的点,若在P中匹配的边和未匹配的边交替出现,则称P为增广路径。可见P的边数一定是奇数,且因为起点和终点都未匹配,所以匹配边比未匹配边少1。

通过将增广路径反色——未匹配边换为匹配边,匹配边换为未匹配边,我们可以得到一个更好的匹配。当没有增广路径时,形成的匹配就是该二分图的最大匹配。

这种算法称为匈牙利算法。


■来一点版题■

◆没有技术含量◆ eXam

只要知道二分图的定义就可以了

这道题只需要判断给出的数据是否合法,且数据完美地分为X部(考试)、Y部(学生),因此就是一个标准的非连通图二分图判断。

考试与学生的关系可以看做连线,这样我们就得到了一个图,其实是多个图。可以通过遍历每一个没有标记的点来判别每一个连通图是否都是二分图,只要有一个不是,就判断"no"。这里作者用vector 的邻接表储存图,col[] 储存标记。

这里的方案其实就是X、Y部中的某一部。(⊙_⊙)

  • 源代码
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int n_cla,n_stu,col[30005],error;
vector<int> vec[30005];
vector<int> ans;
void Solve(int u,int c)
{
col[u]=c;
if(c%2) ans.push_back(u);
for(int i=0;i<(int)vec[u].size();i++)
{
int v=vec[u][i];
if(col[v]){if((c+1)%2!=col[v]%2)error=true;continue;}
Solve(v,c+1);
if(error) return;
}
}
int main()
{
scanf("%d%d",&n_cla,&n_stu);
for(int i=1;i<=n_stu;i++)
{
int A,B;scanf("%d%d",&A,&B);
vec[B].push_back(A);vec[A].push_back(B);
}
for(int i=1;i<=n_cla;i++)
if(!col[i])
{
Solve(i,1);
if(error)
{
puts("no");
return 0;
}
}
printf("yes\n%d\n%d",ans.size(),ans[0]);
for(int i=1;i<(int)ans.size();i++)
printf(" %d",ans[i]);
return 0;
}

◆最大匹配◆ The Perfect Stall

把匈牙利算法的板套上去

  1. 图的建立

虽然二分图分为2个部(牛栏、奶牛),但实际上它还是一个图——所以必须将2个部的点放入同一个图中。这里可以通过将牛放入1~N的点,把牛栏放入N+1~N+M的点中。

2. 匈牙利算法的实现

寻找增广路径一般是采用DFS:

bool DFS(int u)
{
for(int i=0;i<(int)vec[u].size();i++) //枚举邻接点
{
int v=vec[u][i];
if(vis[v]) continue; //之前没有访问
vis[v]=true; //标记
if(mat[v]==0 || DFS(mat[v])) //如果该点没有匹配,或通过该点能向下找到一个未匹配点
{ //这就是一条增广路径
mat[u]=v;mat[v]=u; //反边
return true;
}
}
return false;
}

这道题比较基础,只要求找到最大匹配的数量,因此直接使用匈牙利算法,统计有多少组匹配就可以了。

  • 源代码
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define MAXN 200
int n,m,mat[2*MAXN+5];
bool vis[2*MAXN+5];
vector<int> vec[2*MAXN+5];
bool DFS(int u)
{
for(int i=0;i<(int)vec[u].size();i++)
{
int v=vec[u][i];
if(vis[v]) continue;
vis[v]=true;
if(mat[v]==0 || DFS(mat[v]))
{
mat[u]=v;mat[v]=u;
return true;
}
}
return false;
}
int Solve()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof vis);
if(mat[i]==0 && DFS(i))
ans++;
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
memset(mat,0,sizeof mat);
for(int i=1;i<=n;i++)
{
int n_;scanf("%d",&n_);
for(int j=0,x;j<n_;j++)
{
scanf("%d",&x);
vec[i].push_back(x+n);
vec[x+n].push_back(i);
}
}
printf("%d\n",Solve());
for(int i=1;i<=n;i++) vec[i].erase(vec[i].begin(),vec[i].end());
for(int i=1;i<=m;i++) vec[i+n].erase(vec[i+n].begin(),vec[i+n].end());
}
return 0;
}

◆最小覆盖◆ Machine Schedule

就像一道结论题,结论一发现,就没什么难点了

  1. König定理

最小覆盖点数=最大匹配数

下面是证明:





2. 所以这道题还是最大匹配~

  • 源代码
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define MAXN 200
int n,m,mat[2*MAXN+5];
bool vis[2*MAXN+5];
vector<int> vec[2*MAXN+5];
bool DFS(int u)
{
for(int i=0;i<(int)vec[u].size();i++)
{
int v=vec[u][i];
if(vis[v]) continue;
vis[v]=true;
if(mat[v]==0 || DFS(mat[v]))
{
mat[u]=v;mat[v]=u;
return true;
}
}
return false;
}
int Solve()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof vis);
if(mat[i]==0 && DFS(i))
ans++;
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
memset(mat,0,sizeof mat);
for(int i=1;i<=n;i++)
{
int n_;scanf("%d",&n_);
for(int j=0,x;j<n_;j++)
{
scanf("%d",&x);
vec[i].push_back(x+n);
vec[x+n].push_back(i);
}
}
printf("%d\n",Solve());
for(int i=1;i<=n;i++) vec[i].erase(vec[i].begin(),vec[i].end());
for(int i=1;i<=m;i++) vec[i+n].erase(vec[i+n].begin(),vec[i+n].end());
}
return 0;
}

The End

Thanks for reading!

- Lucky_Glass

【学时总结】 ◆学时·III◆ 二分图的更多相关文章

  1. springMVC文件上传与下载(六)

    1..文件上传 在springmvc.xml中配置文件上传解析器 <!-- 上传图片配置实现类,id必须为这个 --> <bean id="multipartResolve ...

  2. 如何使用和关闭onbeforeunload 默认的浏览器弹窗事件

    Onunload,onbeforeunload都是在刷新或关闭时调用,可以在<script>脚本中通过 window.onunload来指定或者在<body>里指定.区别在于o ...

  3. 教师表(TEACHER.DBF)

    20-27题使用的数据如表1和表2所示. 表1 教师表(TEACHER.DBF) 教师号 姓名 性别 籍贯 职称 年龄 工资/元 0001 王吉兵 男 江苏 讲师 27 2003.50 0002 张晓 ...

  4. 18级北航软件学院算法复习--Samshui

    A 比特手链 简单模拟 判断 贪心 叶姐要想哥赠送一串比特手链,这个手链由0和1组成.想哥买了手链B,无意间得知叶姐想要同样长度的手链A.想哥囊中羞涩,只能手工调整手链.他希望最少通过以下操作进行最少 ...

  5. 【学时总结】 ◆学时 · I◆ A*算法

    [学时·I]A*算法 ■基本策略■ --A*(A Star)无非就是BFS的升级,当BFS都超时的时候-- 同样以队列为基础结构,BFS使用FIFO队列(queue),而A*则使用优先队列(prior ...

  6. 【学时总结&模板时间】◆学时·10 & 模板·3◆ AC自动机

    ◇学时·10 & 模板·3◇ AC自动机 跟着高中上课……讲AC自动机的扩展运用.然而连KMP.trie字典树都不怎么会用的我一脸懵逼<(_ _)> 花一上午自学了一下AC自动机 ...

  7. 【学时总结】◆学时·IX◆ 整体二分

    ◆学时·IX◆ 整体二分 至于我怎么了解到这个算法的……只是因为发现一道题,明显的二分查找,但是时间会爆炸,被逼无奈搜题解……然后就发现了一些东西QwQ ◇ 算法概述 整体二分大概是把BFS与二分查找 ...

  8. 【学时总结】◆学时·VIII◆ 树形DP

    ◆学时·VIII◆ 树形DP DP像猴子一样爬上了树……QwQ ◇ 算法概述 基于树的模型,由于树上没有环,满足DP的无后效性,可以充分发挥其强大统计以及计算答案的能力. 一般来说树形DP的状态定义有 ...

  9. 【学时总结】◆学时·VII◆ 高维DP

    ◆学时·VII◆ 高维DP 自学之余,偶遇DP…… ◇ 算法概述 顾名思义——一种处理多方面状态的DP,这种DP特点是……每一维的大小都不算太大(不然用dp数组存储下来内存会炸),而且枚举时容易超时… ...

随机推荐

  1. Android Weekly Notes Issue #244

    Android Weekly Issue #244 February 12th, 2017 Android Weekly Issue #244 本期内容包括: Android Fragments使用教 ...

  2. Selenium对浏览器的支持

    1.火狐浏览器 优点:FireFox Dirver对页面的自动化测试支持得比较好,很直观地模拟页面的操作,对JavaScript的支持也非常完善,基本上页面上做的所有操作FireFox Driver都 ...

  3. C# 平台问题

    最近在C#项目中嵌入一个视频软件Ffplayer,出现报错现象,提示平台开发视频.dll文件的兼容性和加载格式不正确的问题.最终查看是由于项目平台选择的是Any CPU和X86的引起的.目标平台有什么 ...

  4. asp.net的HTTP请求处理过程

    1.asp.net的HTTP请求处理过程 说明: (1).客户端浏览器向服务器发出一个http请求,此请求会被inetinfo.exe进程截获,然后转交给aspnet_isapi.dll进程,接着它又 ...

  5. 期末作品项目+ppt+设计文档,电子商城的实现,PC+IOS

    先透露几张图 ---- 可以作为文档模板来用... 下载地址 https://dev.tencent.com/u/whuanle/p/IOS_work/attachment/4563020

  6. ASP.NET Core 编码、web编码、网页编码 System.Text.Encodings.Web

    System.Text.Encodings.Web 空间包含表示 Web 编码器的基类.表示 HTML.JavaScript 和 Url 字符编码的子类,以及表示仅允许编码特定字符.字符范围或码位的筛 ...

  7. codeforces 549F Yura and Developers(分治、启发式合并)

    codeforces 549F Yura and Developers 题意 给定一个数组,问有多少区间满足:去掉最大值之后,和是k的倍数. 题解 分治,对于一个区间,找出最大值之后,分成两个区间. ...

  8. requirejs 多页面,多js 打包代码,requirejs多对多打包

    这段代码来自 http://stackoverflow.com/questions/20583812/grunt-requirejs-optimizer-for-a-multi-app-project ...

  9. 关于Class类的getResource().getPath()方法

    程序中配置文件如果放置在classes文件夹,那么我们就可以使用Class类的getResource().getPath()方法获取文件路径. 例如: String path = DBUtil.cla ...

  10. 【转】多线程Core Data

    原文地址:http://www.cocoanetics.com/2012/07/multi-context-coredata/ Multi-Context CoreData When you star ...