专题之匹配网络流
Posted 萌萌的美男子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题之匹配网络流相关的知识,希望对你有一定的参考价值。
1、hdu 2444 The Accomodation of Students(判断二分图+最大匹配)(匈牙利模板)
题意:一共有n个学生,m对关系:A认识B。问能否将所有的人分成两批,每批之间的人都互相认识,如果可以,输出每批的人数。即判断是否为二分图,以及求二分图的最大匹配。
思路:判断是否为二分图(DFS或BFS);求二分图的最大匹配:匈牙利算法。
1 #include<iostream> 2 #include<queue> 3 using namespace std; 4 int n,m; 5 const int maxn = 210;//x集合和y集合总最大的点数 6 bool mp[maxn][maxn];//1表示该ij可以匹配 7 int cx[maxn];//记录x集合中匹配的y元素是哪一个 8 int cy[maxn];//记录y集合中匹配的x元素是哪一个 9 int vis[maxn];//标记该顶点是否访问过 10 int cntx; 11 bool dfs(int u) 12 { 13 for (int v = 1; v <= n; v++)//两个集合内共有n个元素 14 { 15 if (mp[u][v] && !vis[v]) 16 { 17 vis[v] = 1; 18 if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路 19 { 20 cx[u] = v; cy[v] = u; 21 return 1; 22 } 23 } 24 } 25 return 0; 26 } 27 int maxmatch()//匈牙利算法主函数 28 { 29 int ans = 0; 30 memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素! 31 memset(cy, 0xff, sizeof cy); 32 for (int i = 1; i <= n; i++) 33 if (cx[i] == -1)//如果i未匹配 34 { 35 memset(vis, 0, sizeof(vis)); 36 ans += dfs(i); 37 } 38 return ans/2;//对两个部里的都匹配了,这样就相当于匹配了两次了 39 } 40 bool istwo() 41 {//判断是否为二分图 42 queue<int>q; 43 memset(vis, 0, sizeof(vis)); 44 q.push(1); 45 vis[1] = true; 46 while (!q.empty()) 47 { 48 int u = q.front(); 49 q.pop(); 50 for (int i = 1; i <= n; i++) 51 { 52 if (mp[u][i]) 53 { 54 if (vis[i] == 0) 55 { 56 if (vis[u] == 1) vis[i] = 2; 57 else vis[i] = 1; 58 q.push(i); 59 } 60 else 61 { 62 if (vis[i] == vis[u]) return false; 63 } 64 } 65 } 66 } 67 return true; 68 } 69 int main() 70 { 71 while (~scanf("%d%d", &n, &m)) 72 { 73 memset(mp ,0, sizeof(mp)); 74 while (m--) 75 { 76 int a, b; 77 scanf("%d%d", &a, &b); 78 mp[a][b] = mp[b][a] = 1; 79 } 80 if (!istwo()|| n == 1) 81 { 82 printf("No\n"); 83 } 84 else 85 { 86 int ans = maxmatch(); 87 printf("%d\n", ans); 88 } 89 } 90 91 return 0; 92 }
2、hdu 1083 Courses(最大匹配)
题意:有P种课程,N个学生。接下来P行,第i行第一个Ni表示喜欢第i个课程的学生的人数,接下来是Ni个学生。问:能否有一种匹配使得每个学生都选一门不同的课程,同时所有课程都出现。
思路:二分图最大匹配,匈牙利算法,课程为x集,学生为y集。判断最大匹配数是否为P。
1 #include<iostream> 2 #include<queue> 3 #include<memory.h> 4 using namespace std; 5 int n, p; 6 const int maxn = 310;//最大学生数 7 const int maxp = 110;//最大学科数 8 bool mp[maxp][maxn];//1表示该ij可以匹配 9 int cx[maxp];//记录x集合中匹配的y元素是哪一个 10 int cy[maxn];//记录y集合中匹配的x元素是哪一个 11 int vis[maxn];//标记该顶点是否访问过 12 bool dfs(int u) 13 { 14 for (int v = 1; v <= n; v++) 15 { 16 if (mp[u][v] && !vis[v]) 17 { 18 vis[v] = 1; 19 if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路 20 { 21 cx[u] = v; cy[v] = u; 22 return 1; 23 } 24 } 25 } 26 return 0; 27 } 28 int maxmatch()//匈牙利算法主函数 29 { 30 int ans = 0; 31 memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素! 32 memset(cy, 0xff, sizeof cy); 33 for (int i = 1; i <= p; i++) 34 if (cx[i] == -1)//如果i未匹配 35 { 36 memset(vis, 0, sizeof(vis)); 37 ans += dfs(i); 38 } 39 return ans; 40 } 41 42 int main() 43 { 44 int t; 45 scanf("%d", &t); 46 while (t--) 47 { 48 scanf("%d%d", &p, &n); 49 memset(mp, 0, sizeof(mp)); 50 for (int i = 1; i <= p; i++) 51 { 52 int count; 53 scanf("%d", &count); 54 for (int j = 1; j <= count; j++) 55 { 56 int v; 57 scanf("%d", &v); 58 mp[i][v] = true; 59 } 60 } 61 int ans = maxmatch(); 62 if (ans < p)printf("NO\n"); 63 else printf("YES\n"); 64 } 65 66 return 0; 67 }
3、hdu 1281 棋盘游戏(最大匹配)
题意:所有棋子不能同行或同列。给出棋子可以放的位置,问最多能放几个,同时给出重要点(去掉某个位置后就无法保证放尽量多的棋子)的个数。
思路:x集代表行,y集代表列,对于可以放的位置(x,y)将x和y连一条边。最多能放的棋子即该二分图的最大匹配。接下来每次去掉一个位置看看最大匹配是否不变,如果改变则为重要点。
1 #include<iostream> 2 #include<queue> 3 #include<memory.h> 4 using namespace std; 5 int n, m, k; 6 const int maxr = 110;//最大行数 7 const int maxc = 110;//最大列数 8 const int maxk = 10010;//最多可放位置 9 bool mp[maxr][maxc];//1表示该ij可以匹配 10 int cx[maxc];//记录x集合中匹配的y元素是哪一个 11 int cy[maxr];//记录y集合中匹配的x元素是哪一个 12 int vis[maxr];//标记该顶点是否访问过 13 struct point 14 { 15 int x; 16 int y; 17 }points[maxk]; 18 bool dfs(int u) 19 { 20 for (int v = 1; v <= m; v++) 21 { 22 if (mp[u][v] && !vis[v]) 23 { 24 vis[v] = 1; 25 if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路 26 { 27 cx[u] = v; cy[v] = u; 28 return 1; 29 } 30 } 31 } 32 return 0; 33 } 34 int maxmatch()//匈牙利算法主函数 35 { 36 int ans = 0; 37 memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素! 38 memset(cy, 0xff, sizeof cy); 39 for (int i = 1; i <= n; i++) 40 if (cx[i] == -1)//如果i未匹配 41 { 42 memset(vis, 0, sizeof(vis)); 43 ans += dfs(i); 44 } 45 return ans; 46 } 47 48 int main() 49 { 50 int Case = 1; 51 while (~scanf("%d%d%d", &n, &m, &k)) 52 { 53 memset(mp, 0, sizeof(mp)); 54 for (int i = 1; i <= k; i++) 55 { 56 scanf("%d%d", &points[i].x, &points[i].y); 57 mp[points[i].x][points[i].y] = 1; 58 } 59 int ans = maxmatch(); 60 int import = 0; 61 for (int i = 1; i <= k; i++) 62 { 63 mp[points[i].x][points[i].y] = 0; 64 int tmp = maxmatch(); 65 mp[points[i].x][points[i].y] = 1; 66 if (tmp < ans) import++; 67 } 68 printf("Board %d have %d important blanks for %d chessmen.\n", Case++, import, ans); 69 } 70 71 return 0; 72 }
4、HDU 2819 Swap(最大匹配)
题意:给出n*n的矩阵,问能否通过交换某两行或某两列若干次后,使得其对角线上元素均为1.
思路:只交换行或只交换列均可达到一样的效果(如果达不到,说明无解)。列既作为x集,也作为y集,[i][j]连线表示将第j列和第i列互换(当[x][y]为1时,将[x][y]连线,表示将可以其换到[x][x])。然后求此二分图的最大匹配。输出每个匹配时,注意交换,否则会多输出。匈牙利算法。
1 #include<iostream> 2 #include<queue> 3 #include<memory.h> 4 #include<algorithm> 5 using namespace std; 6 int n; 7 const int maxr = 110;//最大行数 8 const int maxc = 110;//最大列数 9 const int maxk = 1010;//最多可放位置 10 bool mp[maxr][maxc];//1表示该ij可以匹配 11 int cx[maxc];//记录x集合中匹配的y元素是哪一个 12 int cy[maxr];//记录y集合中匹配的x元素是哪一个 13 int vis[maxr];//标记该顶点是否访问过 14 struct stp 15 { 16 int x; 17 int y; 18 }stps[maxk]; 19 bool dfs(int u) 20 { 21 for (int v = 1; v <= n; v++) 22 { 23 if (mp[u][v] && !vis[v]) 24 { 25 vis[v] = 1; 26 if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路 27 { 28 cx[u] = v; cy[v] = u; 29 return 1; 30 } 31 } 32 } 33 return 0; 34 } 35 int maxmatch()//匈牙利算法主函数 36 { 37 int ans = 0; 38 memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素! 39 memset(cy, 0xff, sizeof cy); 40 for (int i = 1; i <= n; i++) 41 if (cx[i] == -1)//如果i未匹配 42 { 43 memset(vis, 0, sizeof(vis)); 44 ans += dfs(i); 45 } 46 return ans; 47 } 48 49 int main() 50 { 51 while (~scanf("%d", &n)) 52 { 53 memset(mp, 0, sizeof(mp)); 54 for (int i = 1; i <= n; i++) 55 { 56 for (int j = 1; j <= n; j++) 57 { 58 int v; 59 scanf("%d", &v); 60 if (v > 0) mp[i][j] = 1; 61 } 62 } 63 int ans = maxmatch(); 64 if (ans < n)printf("-1\n"); 65 else 66 { 67 int t = 0; 68 for (int i = 1; i <= n; i++) 69 { 70 int j; 71 for (j = 1; j <= n; j++) if (cy[j] == i)break; 72 if (i != j) 73 { 74 stps[t].x = i, stps[t].y = j; 75 t++; 76 swap(cy[i], cy[j]); 77 } 78 } 79 printf("%d\n", t); 80 for (int i = 0; i < t; i++) 81 { 82 printf("C %d %d\n", stps[i].x, stps[i].y); 83 } 84 } 85 } 86 87 return 0; 88 }
5、hdu 2389 Rain on your Parade(最大匹配)(Hopcroft-Carp算法模板)
题意:有m个人,t时刻后会下雨。地上有n把伞,每把伞只能一个人用。给出人和伞的坐标,以及人的速度,问在下雨前,最多能有多少个人拿到伞?
思路:人作为x集,伞作为y集,如果人能够到达伞的位置,把该人的编号和该伞的编号连线。之后求最大匹配。Hopcroft-Carp算法。
1 #include<iostream> 2 #include<queue> 3 #include<memory.h> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int n,t,m,Nx,Ny,dis; 8 const int maxn = 3100;//最大行数 9 const int maxm = 3100;//最大列数 10 const int maxk = 3100; 11 const int INF = 0x7fffffff; 12 bool mp[maxn][maxm];//1表示该ij可以匹配 13 int cx[maxm];//记录x集合中匹配的y元素是哪一个 14 int dx[maxm]; 15 int cy[maxn];//记录y集合中匹配的x元素是哪一个 16 int dy[maxn]; 17 int vis[maxm];//标记该顶点是否访问过 18 struct point 19 { 20 int x; 21 int y; 22 int v; 23 }customer[maxk]; 24 struct point2 25 { 26 int x; 27 int y; 28 }unbrella[maxk]; 29 bool searchP(void) //BFS 30 { 31 queue <int> Q; 32 dis = INF; 33 memset(dx, -1, sizeof(dx)); 34 memset(dy, -1, sizeof(dy)); 35 for (int i = 1; i <= Nx; i++) 36 if (cx[i] == -1) 37 { 38 Q.push(i); dx[i] = 0; 39 } 40 while (!Q.empty()) 41 { 42 int u = Q.front(); Q.pop(); 43 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 44 for (int v = 1; v <= Ny; v++) 45 if (mp[u][v] && dy[v] == -1) 46 { //v是未匹配点 47 dy[v] = dx[u] + 1; 48 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 49 else 50 { 51 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 52 Q.push(cy[v]); 53 } 54 } 55 } 56 return dis != INF; 57 } 58 59 bool DFS(int u) 60 { 61 for (int v = 1; v <= Ny; v++) 62 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 63 { 64 vis[v] = 1; 65 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 66 if (cy[v] == -1 || DFS(cy[v])) 67 { //是增广路径,更新匹配集 68 cy[v] = u; cx[u] = v; 69 return 1; 70 } 71 } 72 return 0; 73 } 74 75 int MaxMatch(void) 76 { 77 int res = 0; 78 memset(cx, -1, sizeof(cx)); 79 memset(cy, -1, sizeof(cy)); 80 while (searchP()) 81 { 82 memset(vis, 0, sizeof(vis)); 83 for (int i = 1; i <= Nx; i++) 84 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 85 } 86 return res; 87 } 88 89 int main() 90 { 91 int C; 92 scanf("%d", &C); 93 int Case = 1; 94 while (C--) 95 { 96 scanf("%d", &t); 97 memset(mp, 0, sizeof(mp)); 98 scanf("%d", &m); 99 for (int i = 1; i <= m; i++) 100 { 101 scanf("%d%d%d", &customer[i].x, &customer[i].y, &customer[i].v); 102 } 103 scanf("%d", &n); 104 for (int i = 1; i <= n; i++) 105 { 106 scanf("%d%d", &unbrella[i].x, &unbrella[i].y); 107 } 108 for (int i = 1; i <= n; i++) 109 { 110 for (int j = 1; j <= m; j++) 111 { 112 double dis = sqrt((unbrella[i].x - customer[j].x)*(unbrella[i].x - customer[j].x) + (unbrella[i].y - customer[j].y)*(unbrella[i].y - customer[j].y)); 113 if (dis <= t*customer[j].v) mp[i][j] = true; 114 } 115 } 116 Nx = n, Ny = m; 117 int ans = MaxMatch(); 118 printf("Scenario #%d:\n", Case++); 119 printf("%d\n\n", ans); 120 } 121 return 0; 122 }
6、hdu 4185 Oil Skimming
题意:给出一张图,每相邻两个油田可以建立一道沟渠。问最大沟渠的数目。
思路:给每一个油田编号。油田既作为x集,又作为y集,如果两块油田相邻,则连线。之后求最大匹配。
1 #include<iostream> 2 #include<queue> 3 #include<memory.h> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int n, t, m, Nx, Ny, dis; 8 const int maxn = 650;//最大行数 9 const int maxm = 650;//最大列数 10 const int maxk = 360005; 11 const int INF = 0x7fffffff; 12 char M[maxn][maxm]; 13 bool mp[maxn][maxn];//1表示该ij可以匹配 14 int cx[maxk];//记录x集合中匹配的y元素是哪一个 15 int dx[maxk]; 16 int cy[maxk];//记录y集合中匹配的x元素是哪一个 17 int dy[maxk]; 18 int vis[maxk];//标记该顶点是否访问过 19 struct point 20 { 21 int x; 22 int y; 23 }oils[maxk]; 24 bool searchP(void) //BFS 25 { 26 queue <int> Q; 27 dis = INF; 28 memset(dx, -1, sizeof(dx)); 29 memset(dy, -1, sizeof(dy)); 30 for (int i = 1; i <= Nx; i++) 31 if (cx[i] == -1) 32 { 33 Q.push(i); dx[i] = 0; 34 } 35 while (!Q.empty()) 36 { 37 int u = Q.front(); Q.pop(); 38 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 39 for (int v = 1; v <= Ny; v++) 40 if (mp[u][v] && dy[v] == -1) 41 { //v是未匹配点 42 dy[v] = dx[u] + 1; 43 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 44 else 45 { 46 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 47 Q.push(cy[v]); 48 } 49 } 50 } 51 return dis != INF; 52 } 53 54 bool DFS(int u) 55 { 56 for (int v = 1; v <= Ny; v++) 57 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 58 { 59 vis[v] = 1; 60 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 61 if (cy[v] == -1 || DFS(cy[v])) 62 { //是增广路径,更新匹配集 63 cy[v] = u; cx[u] = v; 64 return 1; 65 } 66 } 67 return 0; 68 } 69 70 int MaxMatch(void) 71 { 72 int res = 0; 73 memset(cx, -1, sizeof(cx)); 74 memset(cy, -1, sizeof(cy)); 75 while (searchP()) 76 { 77 memset(vis, 0, sizeof(vis)); 78 for (int i = 1; i <= Nx; i++) 79 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 80 } 81 return res; 82 } 83 84 int main() 85 { 86 int C,N; 87 scanf("%d", &C); 88 int Case = 1; 89 while (C--) 90 { 91 scanf("%d", &N); 92 memset(mp, 0, sizeof(mp)); 93 int cnt = 0; 94 for (int i = 1; i <= N; i++) 95 { 96 for (int j = 1; j <= N; j++) 97 { 98 cin >> M[i][j]; 99 if (M[i][j] == ‘#‘) 100 { 101 oils[cnt].x = i; 102 oils[cnt].y = j; 103 cnt++; 104 } 105 } 106 } 107 for (int i = 0; i < cnt; i++) 108 { 109 for (int j = 0; j < cnt; j++) 110 { 111 if (abs(oils[i].x - oils[j].x) + abs(oils[i].y - oils[j].y) == 1) 112 { 113 mp[i+1][j+1] = 1; 114 } 115 } 116 } 117 Nx = cnt, Ny = cnt; 118 int ans = MaxMatch(); 119 printf("Case %d: %d\n", Case++,ans/2); 120 } 121 return 0; 122 }
7、poj 3020 Antenna Placement(无向二分图的最小路径覆盖)
以上是关于专题之匹配网络流的主要内容,如果未能解决你的问题,请参考以下文章
[kuangbin带你飞]专题十 匹配问题 二分图最大权匹配