二分图模板总结 nyoj 239 月老的难题 && poj 1468 COURSES
来源:互联网 发布:小学教师网络研修计划 编辑:程序博客网 时间:2024/06/05 20:20
二分图最大匹配 匈牙利算法 模板题
月老的难题
- 描述
月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘。
现在,由于一些原因,部分男孩与女孩可能结成幸福的一家,部分可能不会结成幸福的家庭。
现在已知哪些男孩与哪些女孩如果结婚的话,可以结成幸福的家庭,月老准备促成尽可能多的幸福家庭,请你帮他找出最多可能促成的幸福家庭数量吧。
假设男孩们分别编号为1~n,女孩们也分别编号为1~n。
- 输入
- 第一行是一个整数T,表示测试数据的组数(1<=T<=400)
每组测试数据的第一行有两个整数n,K,其中男孩的人数与女孩的人数都是n。(n<=500,K<=10 000)
随后的K行,每行有两个整数i,j表示第i个男孩与第j个女孩有可能结成幸福的家庭。(1<=i,j<=n) - 输出
- 对每组测试数据,输出最多可能促成的幸福家庭数量
- 样例输入
13 41 11 32 23 2
- 样例输出
2
这里用邻接表存储,还可以用邻接矩阵,hash等
这几位大神写的更详细些,可以再参考参考:二分图的最大匹配
匈牙利算法求二分图的最大匹配
匈牙利算法模板
模板中核心部分都一样,我用的int 型map和vis数组(vector中用link)进行标记(0或1),其实也可以用bool 型,这样更能节省空间
#include<algorithm>#include<cstring>#include<stdio.h>#include<vector>using namespace std;#define MAXN 550vector<int> v[MAXN];/*用STL中的vector建立邻接表实现匈牙利算法效率比较高*/int vis[MAXN]; //记录v[x]中结点是否访问过int link[MAXN];int n,m;bool find(int x){ for(int j= 0 ; j < v[x].size(); j++) { if(vis[v[x][j]]==0)//如果节点i与j相连并且未被查找过 { vis[v[x][j]]=1; //标记i为已查找过 if(link[v[x][j]]==0||find(link[v[x][j]])) { //如果j是起始节点或者与j相连的结点出发有增广路径 link[v[x][j]]=x; return true; } } } return false;}int MaxMatch(){ memset(link,0,sizeof(link)); int count = 0 ; for(int i = 1; i <= n ; i++) { memset(vis,0,sizeof(vis)); if(find(i)) count ++; } return count;}int main(){ int N,star,to; scanf("%d",&N); while(N--) { memset(v,0,sizeof(v)); scanf("%d%d",&n,&m); for(int i = 0 ; i < m ; i++) { scanf("%d%d",&star,&to); v[star].push_back(to); } printf("%d\n",MaxMatch()); } return 0;}
若用邻接矩阵存,超时,不过这种方法可以做 poj1469 COURSES 这题
#include<algorithm>#include<cstring>#include<stdio.h>#include<vector>using namespace std;#define MAXN 550int map[MAXN][MAXN];int vis[MAXN];int link[MAXN];int n,m;bool find(int x){ for(int i = 1; i <= m;i++) { if(vis[i]==0&&map[x][i]) { vis[i]=1; if(link[i]==0 || find(link[i])) { link[i]=x; return true; }
} } return false ;}int MaxMatch(){ memset(link,0,sizeof(link)); int count = 0 ; for(int i = 1; i <= n ; i++) { memset(vis,0,sizeof(vis)); if(find(i)) count ++; } return count ;}int main(){ int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); memset(map,0,sizeof(map)); for(int i = 1 ; i <= n ; i++) { int t; scanf("%d",&t); for(int j = 1 ; j <= t ; j++){ int k ; scanf("%d",&k); map[i][k]=1; } } if(MaxMatch()== n) printf("YES\n"); else printf("NO\n"); } return 0;}
变1 :
在二分图中求最少的点,让每条边都至少和其中的一个点关联,这就是“二分图的最小顶点覆盖”:
二分图的最小顶点覆盖数 = 二分图的最大匹配数
例题:hdu 1150 Machine Schedule
#include<algorithm>#include<cstring>#include<stdio.h>#include<vector>using namespace std;#define MAXN 550int map[MAXN][MAXN];int vis[MAXN];int link[MAXN];int n,m;bool find(int x){ for(int i = 1; i <= n; i++) { if(vis[i]==0&&map[x][i]==1 ) { vis[i]=1; if(link[i]==0 || find(link[i])) { link[i]=x; return true; } } } return false ;}int MaxMatch(){ memset(link,0,sizeof(link)); int count = 0 ; for(int i = 1; i <= m ; i++)//这里的m和find函数里的n可以互换 {//原因是都是在二分图的一个集合中遍历另一个集合的最优匹配 memset(vis,0,sizeof(vis));//所以两个集合谁遍历谁都一样 if(find(i)) count ++; } return count ;}int main(){ while(scanf("%d",&n),n) { int k; scanf("%d%d",&m,&k); memset(map,0,sizeof(map)); for(int i = 1 ; i <= k ; i++) { int a,b,c; scanf("%d%d%d",&c,&a,&b); map[a][b]=1; } printf("%d\n",MaxMatch()); } return 0;}
变2:DAG图(无回路有向图)的最小路径覆盖
用尽量少的不相交简单路径覆盖有向无环图(DAG)的所有顶点,这就是DAG图的最小路径覆盖问题。
DAG图的最小路径覆盖数 = 节点数(n)- 最大匹配数(m)
例题:hdu 1151 Air Raid
这题直接套前面的模板就行,把输入格式改改,最后用n - MaxMatch()得到答案
变3: 二分图的最大独立集
二分图的最大独立集数 = 节点数(n)- 最大匹配数(m)
关键:求二分图的最大匹配数
例题:hdu 1068 Girls and Boys
此题相当于有向图,最大匹配/2
#include<algorithm>#include<cstring>#include<stdio.h>#include<vector>using namespace std;#define MAXN 550int map[MAXN][MAXN];int vis[MAXN];int link[MAXN];int n,m;bool find(int x){ for(int i = 0; i < n; i++) { if(vis[i]==0&&map[x][i]==1 ) { vis[i]=1; if(link[i]==0 || find(link[i])) { link[i]=x; return true; } } } return false ;}int MaxMatch(){ memset(link,0,sizeof(link)); int count = 0 ; for(int i = 0; i < n ; i++) { memset(vis,0,sizeof(vis)); if(find(i)) count ++; } return count ;}int main(){//注意每一题的取值范围,此题为0--n-1 while(scanf("%d",&n)!=EOF) { memset(map,0,sizeof(map)); for(int i = 0 ; i < n ; i++) { int a,b,c; scanf("%d: (%d)",&a,&b); for(int j = 0 ; j < b ; j++) { scanf("%d",&c); map[a][c]=1; } } printf("%d\n",(n-MaxMatch()/2));//这题相当于有向图,循环两遍因此除以2 } return 0;}
- 二分图模板总结 nyoj 239 月老的难题 && poj 1468 COURSES
- NYOJ 题目239 月老的难题 (二分图最大匹配-匈牙利算法模板)
- NYOJ 239 月老的难题(二分图匹配)
- NYOJ 239 月老的难题(二分图最大匹配)
- nyoj 239 月老的难题<二分图匹配>
- NYoj 239 :月老的难题(二分图最大匹配)
- NYOJ 239 月老的难题(最大二分图匹配)
- NYOJ 237 NYOJ 239 二分图 最大匹配模板题 游戏高手的烦恼 月老的难题 两个题一样
- 二分图匹配算法之匈牙利算法模板 hdoj1083 nyoj月老的难题
- NYOJ-239 月老的难题
- NYOJ 239 月老的难题
- NYOJ 239 月老的难题
- NYOJ 239-月老的难题
- nyoj 239 月老的难题
- NYOJ-239 月老的难题
- nyoj 239 月老的难题
- nyoj 239 月老的难题 【二分匹配之匈牙利】
- nyoj 239 月老的难题 二分图最大匹配(匈牙利算法)
- Datalink Access
- PHP判断PC端和手机端
- Selenium RC工作原理
- PowerDesigner反向生成物理数据模型
- LIB和DLL的区别与使用
- 二分图模板总结 nyoj 239 月老的难题 && poj 1468 COURSES
- 带时间戳算法-----类似authcode开源算法
- Cracking the coding interview
- Selenium IDE的使用
- Yii框架zii.widgets.grid自定义按钮,ajax触发事件并提示
- Notification各种用法源码详解
- android 飞行模式 注册广播后 三种状态监听
- 上传图片最大允许的边长Ueditor
- git服务器搭建过程