二分匹配mark
来源:互联网 发布:js游戏引擎 编辑:程序博客网 时间:2024/05/29 13:17
二分图:一个无向图的顶点可以分割成两个互不相交的顶点集称为二分图。
若全部的点都匹配了称为完美匹配;
存在一条y1-x1-y2-x2(如图)的路径则称交替路径。
若路径的起点和终点未被匹配则存在一条增广路径。
则可使匹配数增加 1 ;
berge定理:若匹配M为最大匹配当且仅当二分图中不存在增广路径
原代码链接
/* **************************************************************************//二分图匹配(匈牙利算法的DFS实现)//初始化:g[][]两边顶点的划分情况//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配//g没有边相连则初始化为0//uN是匹配左边的顶点数,vN是匹配右边的顶点数//调用:res=hungary();输出最大匹配数//优点:适用于稠密图,DFS找增广路,实现简洁易于理解//时间复杂度:O(VE)//***************************************************************************///顶点编号从0开始的const int MAXN=510;int uN,vN;//u,v数目int g[MAXN][MAXN];int linker[MAXN];bool used[MAXN];bool dfs(int u)//从左边开始找增广路径{ int v; for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改 if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) {//找增广路,反向 linker[v]=u; return true; } } return false;//这个不要忘了,经常忘记这句}int hungary(){ int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u)) res++; } return res;}
/* *********************************************二分图匹配(Hopcroft-Carp的算法)。初始化:g[][]邻接矩阵调用:res=MaxMatch(); Nx,Ny要初始化!!!时间复杂大为 O(V^0.5 E) 适用于数据较大的二分匹配需要queue头文件********************************************** */const int MAXN=3000;const int INF=1<<28;int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;int dx[MAXN],dy[MAXN],dis;bool vst[MAXN];bool searchP(){ queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<Nx;i++) if(Mx[i]==-1) { Q.push(i); dx[i]=0; } while(!Q.empty()) { int u=Q.front(); Q.pop(); if(dx[u]>dis) break; for(int v=0;v<Ny;v++) if(g[u][v]&&dy[v]==-1) { dy[v]=dx[u]+1; if(My[v]==-1) dis=dy[v]; else { dx[My[v]]=dy[v]+1; Q.push(My[v]); } } } return dis!=INF;}bool DFS(int u){ for(int v=0;v<Ny;v++) if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1) { vst[v]=1; if(My[v]!=-1&&dy[v]==dis) continue; if(My[v]==-1||DFS(My[v])) { My[v]=u; Mx[u]=v; return 1; } } return 0;}int MaxMatch(){ int res=0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(searchP()) { memset(vst,0,sizeof(vst)); for(int i=0;i<Nx;i++) if(Mx[i]==-1&&DFS(i)) res++; } return res;}
最小路径覆盖数= 顶点数-最大匹配数
例题 HDU1151
题目大意:在一个城镇,有m个路口,和n条路,这些路都是单向的,而且路不会形成环,现在要弄一些伞兵去巡查这个城镇,伞兵只能沿着路的方向走,问最少需要多少伞兵才能把所有的路口搜一遍。
(其实就是给一个m个点n条边的有向无环图,求该图的最小路径覆盖)
#include<stdio.h>#include<string.h>#define N 202int use[N]; //记录y中节点是否使用int link[N]; //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm; //二分图中x和y中点的数目int can(int t){ int j; for(j=1;j<=gn;j++) { if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边 { use[j]=1;//标记 if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点 { //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点 link[j]=t; // link[j] 连接下一个相连的点; return 1; } } } return 0;}int MaxMatch(){ int i,num; num=0; memset(link,0,sizeof(link)); for(i=1;i<=gn;i++)//x中 第i个点 { memset(use,0,sizeof(use)); if(can(i)) num++; } return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int t,k;scanf("%d",&t);while(t--){init();scanf("%d%d",&gn,&k);while(k--){int a,b;scanf("%d%d",&a,&b);mat[a][b]=1;}printf("%d\n",gn-MaxMatch());}return 0;}
/*******************************************最大匹配数:匈牙利算法HDU 1281 车不能相互攻击则 坐标(x,y)上x只能放一个棋子,y上只能放一个棋子所以一个x对应着一个y,其他棋子不能放在x上或者y上.这样就是二分匹配了重要点:则是不可替代的点,所以拿去这个点看最大匹配数有无减少********************************************/#include<stdio.h>#include<string.h>#define N 202int use[N]; //记录y中节点是否使用int link[N]; //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm; //二分图中x和y中点的数目int can(int t){ int j; for(j=1;j<=gn;j++) { if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边 { use[j]=1;//标记 if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点 { //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点 link[j]=t; // link[j] 连接下一个相连的点; return 1; } } } return 0;}int MaxMatch(){ int i,num; num=0; memset(link,0,sizeof(link)); for(i=1;i<=gn;i++)//x中 第i个点 { memset(use,0,sizeof(use)); if(can(i)) num++; } return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int n,m,k,cas=1,x,y;while(~scanf("%d%d%d",&n,&m,&k)){gn=n;gm=m;init();for(int i=0;i<k;i++){scanf("%d%d",&x,&y);mat[x][y]=1;}int ans=MaxMatch(),sum=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(mat[i][j]){mat[i][j]=0;if(MaxMatch()!=ans)sum++;mat[i][j]=1;}}}printf("Board %d have %d important blanks for %d chessmen.\n",cas++,sum,ans);}return 0;}
HDU 1498
/*******************************************最大匹配数:匈牙利算法HDU 1498 题意:给出一个矩阵 一次可以消除一行或者一列的同种颜色的气球;如果可以在k次之内不能消完这种颜色的气球 则记录下;********************************************/#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define N 202int use[N]; //记录y中节点是否使用int link[N]; //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm; //二分图中x和y中点的数目int can(int t){ int j; for(j=1;j<=gn;j++) { if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边 { use[j]=1;//标记 if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点 { //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点 link[j]=t; // link[j] 连接下一个相连的点; return 1; } } } return 0;}int MaxMatch(){ int i,num; num=0; memset(link,0,sizeof(link)); for(i=1;i<=gn;i++)//x中 第i个点 { memset(use,0,sizeof(use)); if(can(i)) num++; } return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int n,k,i,j,l;int map[N][N];while(scanf("%d%d",&n,&k),n+k){int ans[N]={0},in[N*N+11],num1=0,num2=0;bool vis[N*N];memset(vis,false,sizeof(vis));memset(map,0,sizeof(map));for(i=1;i<=n;i++){for( j=1;j<=n;j++){scanf("%d",&map[i][j]); //输入矩阵if(!vis[map[i][j]]){in[num1++]=map[i][j]; //记录出现的气球颜色并记录vis[map[i][j]]=true; }}}gn=gm=n;for(i=0;i<num1;i++){init();for(j=1;j<=n;j++) //建二分图{for(l=1;l<=n;l++){if(map[j][l]==in[i])mat[j][l]=1;}}if(k<MaxMatch()) //如果匹配数大于k次则记录ans[num2++]=in[i];}sort(ans,ans+num2);//输出要排序if(num2==0)printf("-1\n");for(i=0;i<num2;i++)printf("%d%c",ans[i],i==num2-1?'\n':' ');}return 0;}
HDU 1507
/******************************************* 最大匹配数:匈牙利算法 HDU 1507题意:最大匹配给出一些不能放的格子 剩下的放置 2x1的格子问最大多少可以放几个2x1格子并输出********************************************/#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>#include <vector>using namespace std;const int MAXN = 510;int uN,vN;//u,v的数目,使用前面必须赋值int g[MAXN][MAXN];//邻接矩阵int linker[MAXN];bool used[MAXN];bool dfs(int u){ for(int v = 0; v < vN;v++) if(g[u][v] && !used[v]) { used[v] = true; if(linker[v] == -1 || dfs(linker[v])) { linker[v] = u; return true; } }return false;}int MaxMatch(){ int res = 0; memset(linker,-1,sizeof(linker)); for(int u = 0;u < uN;u++){ memset(used,false,sizeof(used)); if(dfs(u))res++; } return res;}int a[110][110];int b[100];int main(){ int n,m,k; int u,v; while(scanf("%d%d",&n,&m),n+m) { scanf("%d",&k); memset(a,0,sizeof(a)); while(k--){ scanf("%d%d",&u,&v); u--,v--; a[u][v]=-1; } int index = 0; for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++)if(a[i][j]!=-1){b[index] = i*m + j;a[i][j] = index++;}}uN = vN = index;memset(g,0,sizeof(g));for(int i = 0;i < n;i++){//建立匹配图for(int j= 0;j < m;j++){if(a[i][j]!=-1 && (i+j)%2==1){u = a[i][j];if(i > 0 && a[i-1][j]!=-1)g[u][a[i-1][j]]=1;if(i < n-1 && a[i+1][j]!=-1)g[u][a[i+1][j]]=1;if(j > 0 && a[i][j-1]!=-1)g[u][a[i][j-1]]=1;if(j < m-1 && a[i][j+1]!=-1)g[u][a[i][j+1]]=1;}}}int ans = MaxMatch();printf("%d\n",ans);for(int i = 0;i <vN;i++){if(linker[i]!=-1){int x1 = b[i]/m;int y1 = b[i]%m;int x2 = b[linker[i]]/m;int y2 = b[linker[i]]%m;printf("(%d,%d)--(%d,%d)\n",x1+1,y1+1,x2+1,y2+1);}}printf("\n"); } return 0;}
0 0
- 二分匹配mark
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 二分匹配
- 【藏龙卧虎】成都传智播客Java就业班火爆开班!
- VC应用程序上引用动画的效果
- Java实现的苹果IAP二次验证主要逻辑
- java Integer和int之间==的比较问题
- restclient测试工具
- 二分匹配mark
- android中Invalidate和postInvalidate的区别
- cocosbuilder源代码分析
- vi 批量加注释‘#’
- Sping中使用单实例化简化多线程的相关实现 事务 模板 回调
- httpsqs一个不错的消息队列,值得尝试
- 教你怎么挖到精准微信用户
- 网站提高速度的13个简易规则
- esayui 常见 学习网站 .