【学时总结】 ◆学时·III◆ 二分图
【学时·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
只要知道二分图的定义就可以了
- SGU - 172
- 解析
这道题只需要判断给出的数据是否合法,且数据完美地分为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
把匈牙利算法的板套上去
- POJ - 1274
- 解析
- 图的建立
虽然二分图分为2个部(牛栏、奶牛),但实际上它还是一个图——所以必须将2个部的点放入同一个图中。这里可以通过将牛放入1~N的点,把牛栏放入N+1~N+M的点中。
- 匈牙利算法的实现
寻找增广路径一般是采用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
就像一道结论题,结论一发现,就没什么难点了
- POJ - 1325
- 解析
- König定理
最小覆盖点数=最大匹配数
下面是证明:
- 所以这道题还是最大匹配~
- 源代码
/*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
转载于:https://www.cnblogs.com/LuckyGlass-blog/p/9061140.html
【学时总结】 ◆学时·III◆ 二分图相关推荐
- python程序设计的基本步骤_Python程序设计课程教与学(54、72、80学时教学大纲)...
原标题:Python程序设计课程教与学(54.72.80学时教学大纲) <算法与程序设计>课程教学大纲 Programming and Algorithm Fundamentals (Py ...
- 计算机网络技术实验,计算机网络技术实验
计算机网络技术实验 实 验 一 (2课时) 课题(项目)名称: 基本网络命令 计划学时: 2学时 实验目的 1. 了解网络命令的基本使用方法 2. 掌握常用网络命令的应用 3. 掌握网络通达性的基本测 ...
- 大学 C语言程序设计第一讲,c语言程序设计1第一讲(第一章上).ppt
c语言程序设计1第一讲(第一章上).ppt 知识可以改变命运! 高级语言程序设计 主讲教师:贾彩燕 计算机与信息技术学院 计算机科学与技术系 cyjia@bjtu.edu.cn 计算机与人沟通 计算机 ...
- 复旦大学《数学分析》教学大纲,读后有感
该<分析>大纲读后,犹如时间倒转,回到19世纪的马克思撰写<数学手稿>时代,-- 但是,时光不能倒流.进入20世纪,数学公理化时代终于到来了.1930年,哥德尔紧致性定理:19 ...
- c语言 如何读多种数据类型 非类,c语言程序设计教学大纲(非电气类)文档.doc
c语言程序设计教学大纲(非电气类)文档 <C语言程序设计>课程教学大纲 主任 教研室主任 大纲执笔人 姜长洪 王海荣 C语言备课组 一.课程基本信息 课程编号:×××× 课程名称:C语言程 ...
- 有n个学生选修了c语言程序设计这门课程,C语言程序设计报告学生选修课系统(18页)-原创力文档...
C 语 言 程 序 设 计 学校: 学院: 班级序号: 学号: :姓名 指导老师: C语言程序设计报告 一.C语言课程设计的目的: 高级语言课程设计是学习完<高级语言程序设计>课程后进行的 ...
- 实验3 数据库综合查询
实验3 数据库综合查询 一.实验目的 掌握SELECT语句的基本语法和查询条件表示方法: 掌握查询条件种类和表示方法: 掌握连接查询的表示及使用: 掌握嵌套查询的表示及使用: 了解集合查询的表示及使 ...
- 实验七:层叠样式表(二)
1 学时 2学时 实验目的与要求 (1)熟悉掌握CSS文件编写的语法规则: (2)熟练使用编辑工具编写CSS文件: (3)熟练掌握使用CSS格式化XML文档的过程. (4)认真在编辑环境中编写CSS文 ...
- 实验三:XML模型(一)
实验三:XML模型(一) 1 学时 2学时 2.实验目的与要求 通过实验,使学生理解XML模型的概念:能够掌握DTD的基本语法.作用和使用方式.要求学生能够根据XML文档推出满足要求的DTD文件,并 ...
最新文章
- php 开发restful api,用PHP创建RESTful API?
- 邮件整体解决方案_面向未来的冻干机进出料解决方案:阿尔法(ALUS)系列自动进出料系统...
- 【深度学习】常见优化器的PyTorch实现
- apache php 单入口,apache配置php实现单一入口方法
- Asp.net 文件上传的 FileUpload FileName 和 FileUpload PostedFile.FileName的细节问题
- github最值得收藏的Bootstrap3后台管理框架
- ANTLR 4(一)Getting Started
- LeetCode 2020 力扣杯全国秋季编程大赛(656/3244,前20.2%)
- nagios常见问题
- Vivado生成bit文件出现error解决
- python 生成器原理_python生成器
- 使用DPM2007来保护企业数据
- 内网SMTP发送失败的曲线救国之策
- vs2008 sp1下载中
- 教你如何免费下载中国知网、万方学术论文?这份资源请收好!
- Apache解析漏洞
- 哔哩哔哩我来了,see goodbye 马总!!!
- 利用互斥量实现进程间同步
- HTTP协议的理解和使用
- 图解TCP/IP之半双工全双工通信