二分图匹配入门题
Posted N_Yokel
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分图匹配入门题相关的知识,希望对你有一定的参考价值。
板子(匈牙利算法,邻接矩阵)
const int MAXN=2e3+5; int uN, vN; 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 hungary() { int res=0; memset(linker, -1, sizeof(linker)); for(int u=0; u<uN; u++) { memset(used, 0, sizeof(used)); if(dfs(u)) res++; } return res; }
HDU 1045
题意:给出一张图,给出空地‘.‘和隔板‘x’, 求放置最多满足条件的blockhouse,条件:垂直和水平方向上没有如果隔板隔开的话,只能放置一个house,有隔板话,隔板的之后(相对位置)的不用考虑。
题解:分别对每一行和每一列进行缩点(重新标号),两个相交的话就连边(其实就是把2个条件链接(行,列)到了一起)
#include <bits/stdc++.h> using namespace std; #define _for(i,a,b) for(int i=(a); i< (b); i++) #define _rep(i,a,b) for(int i=(a); i<=(b); i++) const int MAXN=2e2+5; int uN, vN; 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 hungary() { int res=0; memset(linker, -1, sizeof(linker)); for(int u=0; u<uN; u++) { memset(used, 0, sizeof(used)); if(dfs(u)) res++; } return res; } char Map[MAXN][MAXN]; int Mrow[MAXN][MAXN], Mcol[MAXN][MAXN]; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); //freopen("in.txt", "r", stdin); int n; while(cin>>n, n) { _for(i, 0, n) _for(j, 0, n) cin>>Map[i][j]; uN=vN=0; int cu=0, cv=0; memset(Mrow, -1, sizeof(Mrow)); memset(Mcol, -1, sizeof(Mcol)); _for(i, 0, n) _for(j, 0, n) { if(Mrow[i][j]==-1 && Map[i][j]==‘.‘) { for(int k=j; k<n&&Map[i][k]==‘.‘; k++) Mrow[i][k]=cu; uN=max(uN, ++cu); } if(Mcol[i][j]==-1 && Map[i][j]==‘.‘) { for(int k=i; k<n&&Map[k][j]==‘.‘; k++) Mcol[k][j]=cv; vN=max(vN, ++cv); } } memset(g, 0, sizeof(g)); _for(i, 0, n) _for(j, 0, n) if(Map[i][j]==‘.‘) g[Mrow[i][j]][Mcol[i][j]]=1; cout<<hungary()<<endl; } return 0; }
HDU 2444
题意:给你一张图,你需要先判断这个图是不是二分图,然后在求其的最大匹配。
题解:用bfs对图进行染色,如果发现有相邻且同色的点那么就不是二分图。
#include <bits/stdc++.h> using namespace std; const int MAXN=200+5; vector<int> G[MAXN]; int uN, linker[MAXN]; bool used[MAXN]; bool dfs(int u) { for(int i=0; i<G[u].size(); i++) { int v=G[u][i]; if(!used[v]) { used[v]=true; if(linker[v]==-1 || dfs(linker[v])) { linker[v]=u; return true; } } } return false; } int hungary() { int res=0; memset(linker, -1, sizeof(linker)); for(int u=1; u<=uN; u++) { memset(used, false, sizeof(used)); if(dfs(u)) res++; } return res; } int vis[MAXN]; bool check() { queue<int> q; memset(vis, false, sizeof(vis)); q.push(1); vis[1]=true; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<G[u].size(); i++) { int v=G[u][i]; if(!vis[v]) { if(vis[u]==1) vis[v]=2; else vis[v]=1; //这里的染色别弄错了 //if(vis[u]) vis[v]=2; else vis[v]=1; WA q.push(v); } else if(vis[u]==vis[v]) return false; } } return true; } int main() { //freopen("in.txt", "r", stdin); int n, m; while(cin>>n>>m) { uN=n; for(int i=0; i<=n; i++) G[i].clear(); while(m--) { int u, v; cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } if(check()) cout<<hungary()/2<<endl; else cout<<"No"<<endl; } return 0; }
HDU 1281
题意:
小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的攻击。
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。 Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么。求多少个这样的点,和最大可以放下的车。
题解:把行看成x部,列看成y部,可以放车的点的坐标,即x,y可以构成一个匹配(和 HDU 1045一样的道理),每一行每一列只有一个车,相当于行和列在做匹配,在尝试接触g【x】【y】看求出的最大匹配是否发生变化。若发生变化,即是重要点。
#include <bits/stdc++.h> using namespace std; const int MAXN=100+5; int uN, vN; 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 hungary() { int res=0; memset(linker, -1, sizeof(linker)); for(int u=0; u<uN; u++) { memset(used, 0, sizeof(used)); if(dfs(u)) res++; } return res; } int main() { int n, m, k, kase=0; while(cin>>n>>m>>k) { uN=n, vN=m; memset(g, 0, sizeof(g)); while(k--) { int u, v; cin>>u>>v; g[u-1][v-1]=1; } int ans=hungary(); int res=0; for(int i=0; i<n; i++) for(int j=0; j<m; j++) { if(g[i][j]) { g[i][j]=0; if(ans>hungary()) res++; g[i][j]=1; } } printf("Board %d have %d important blanks for %d chessmen.\n",++kase, res, ans); } return 0; }
HDU 2819
题意:可交换任意两行或任意两列,最终是主对角线上全为1,输出交换过程(如果可以的话),否则-1
题解:如果可行的话,一定可以只交换列或只交换行得到,因为不管怎么交换,原先在同一行的始终在同一行,原先在同一列的始终在同一列。用行列构图(和上面的一样),如果a[i][j]=1,则加边,求最大匹配就行。
输出交换的路径的时候,注意这个交换中的行列是指在原图的中的位置。
注意:解除流绑定后,就要不要用printf了,用printf就不要接触流绑定,会WA
#include <bits/stdc++.h> using namespace std; const int MAXN=200+5; int uN, vN; 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 hungary() { int res=0; memset(linker, -1, sizeof(linker)); for(int u=0; u<uN; u++) { memset(used, 0, sizeof(used)); if(dfs(u)) res++; } return res; } int A[MAXN*MAXN], B[MAXN*MAXN]; int main() { //freopen("in.txt", "r", stdin); //ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n; while(cin>>n) { uN=vN=n; for(int i=0; i<n; i++) for(int j=0; j<n; j++) cin>>g[i][j]; if(hungary()<n){ cout<<"-1"<<endl; continue; } int cnt=0; for(int j, i=0; i<uN; i++) //记录匹配过程中的匹配路径 { for(j=0; j<vN&&linker[j]!=i; j++) ; if(i!=j){ //交换i,j。这个交换中的行列是指在原图的中的位置 A[cnt]=i, B[cnt++]=j; swap(linker[i], linker[j]); } } cout<<cnt<<endl; for(int i=0; i<cnt; i++) printf("C %d %d\n", A[i]+1, B[i]+1); } return 0; }
以上是关于二分图匹配入门题的主要内容,如果未能解决你的问题,请参考以下文章