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 "); 83 } 84 else 85 { 86 int ans = maxmatch(); 87 printf("%d ", 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 "); 63 else printf("YES "); 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. ", 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 "); 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 ", t); 80 for (int i = 0; i < t; i++) 81 { 82 printf("C %d %d ", 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: ", Case++); 119 printf("%d ", 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 ", Case++,ans/2); 120 } 121 return 0; 122 }
7、poj 3020 Antenna Placement(无向二分图的最小路径覆盖)
题意:每个天线最多只能覆盖自己所在的格子和一个相邻的位置(可以是城市也可以是空地)。问最少的天线数目。
思路:把每个城市拆成两点,让其既属于x集又属于y集,然后把相邻的城市连线。计算最大匹配,然后用总城市数-最大匹配数即得最小路径覆盖(用最少的边,覆盖所有顶点。每个顶点初始各有一条边,如果两个点相连,则为了使答案中的每条边都不能交于相同一点,所以要减1.最后即减去最大匹配数).
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,totalr,totalc; 8 const int maxn = 50;//最大行数 9 const int maxm = 15;//最大列数 10 const int maxk = 1000; 11 const int INF = 0x7fffffff; 12 int M[maxn][maxm]; 13 bool mp[maxk][maxk];//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 int dr[] = { 0,0,1,-1 }; 20 int dc[] = { 1,-1,0,0 }; 21 struct point 22 { 23 int x; 24 int y; 25 }oils[maxk]; 26 bool searchP(void) //BFS 27 { 28 queue <int> Q; 29 dis = INF; 30 memset(dx, -1, sizeof(dx)); 31 memset(dy, -1, sizeof(dy)); 32 for (int i = 1; i <= Nx; i++) 33 if (cx[i] == -1) 34 { 35 Q.push(i); dx[i] = 0; 36 } 37 while (!Q.empty()) 38 { 39 int u = Q.front(); Q.pop(); 40 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 41 for (int v = 1; v <= Ny; v++) 42 if (mp[u][v] && dy[v] == -1) 43 { //v是未匹配点 44 dy[v] = dx[u] + 1; 45 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 46 else 47 { 48 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 49 Q.push(cy[v]); 50 } 51 } 52 } 53 return dis != INF; 54 } 55 56 bool DFS(int u) 57 { 58 for (int v = 1; v <= Ny; v++) 59 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 60 { 61 vis[v] = 1; 62 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 63 if (cy[v] == -1 || DFS(cy[v])) 64 { //是增广路径,更新匹配集 65 cy[v] = u; cx[u] = v; 66 return 1; 67 } 68 } 69 return 0; 70 } 71 72 int MaxMatch() 73 { 74 int res = 0; 75 memset(cx, -1, sizeof(cx)); 76 memset(cy, -1, sizeof(cy)); 77 while (searchP()) 78 { 79 memset(vis, 0, sizeof(vis)); 80 for (int i = 1; i <= Nx; i++) 81 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 82 } 83 return res; 84 } 85 86 int main() 87 { 88 int C, N; 89 scanf("%d", &C); 90 int Case = 1; 91 while (C--) 92 { 93 scanf("%d%d", &totalr,&totalc); 94 memset(mp, 0, sizeof(mp)); 95 int cnt = 0; 96 for (int i = 1; i <= totalr; i++) 97 { 98 for (int j = 1; j <= totalc; j++) 99 { 100 char c; 101 cin >> c; 102 if (c == 'o') 103 { 104 M[i][j] = 0; 105 } 106 else 107 { 108 M[i][j] = ++cnt; 109 } 110 } 111 } 112 // //通过“拆点”操作,把每一个城市拆分为2个,分别属于所构造的二分图的两个点集 113 for (int i = 1; i <= totalr; i++) 114 { 115 for (int j = 1; j <= totalc; j++) 116 { 117 if (M[i][j] > 0) 118 { 119 int u = M[i][j]; 120 for (int k = 0; k < 4; k++) 121 { 122 int tr = i + dr[k]; 123 int tc = j + dc[k]; 124 if (tr >= 1 && tr <= totalr&&tc >= 1 && tc <= totalc) 125 { 126 if (M[tr][tc] > 0) 127 { 128 mp[u][M[tr][tc]] = true; 129 } 130 } 131 } 132 } 133 } 134 } 135 136 Nx = cnt, Ny = cnt; 137 int ans = MaxMatch(); 138 printf("%d ", cnt-ans / 2);//无向二分图:最小路径覆盖数 = "拆点"前原图的顶点数 - 最大匹配数/2 139 } 140 return 0; 141 }
8、hdu 1054 Strategic Game POJ1463
题意:有一棵树,现在需要在树上结点的位置放士兵,每个士兵能够守卫与其所在顶点相连接的边。问最少放多少士兵,能够守卫所有的边。
思路:每个点既属于x集,又属于y集,两点之间若有边则连线。计算最大匹配。(如果a和b、c、d相连,那么如果选择a和b相连的这条边,则不能再选a点,也就不能再选和a连接的边)
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, totalr, totalc; 8 const int maxn = 50;//最大行数 9 const int maxm = 15;//最大列数 10 const int maxk = 1505; 11 const int INF = 0x7fffffff; 12 bool mp[maxk][maxk];//1表示该ij可以匹配 13 int cx[maxk];//记录x集合中匹配的y元素是哪一个 14 int dx[maxk]; 15 int cy[maxk];//记录y集合中匹配的x元素是哪一个 16 int dy[maxk]; 17 int vis[maxk];//标记该顶点是否访问过 18 bool searchP(void) //BFS 19 { 20 queue <int> Q; 21 dis = INF; 22 memset(dx, -1, sizeof(dx)); 23 memset(dy, -1, sizeof(dy)); 24 for (int i = 1; i <= Nx; i++) 25 if (cx[i] == -1) 26 { 27 Q.push(i); dx[i] = 0; 28 } 29 while (!Q.empty()) 30 { 31 int u = Q.front(); Q.pop(); 32 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 33 for (int v = 1; v <= Ny; v++) 34 if (mp[u][v] && dy[v] == -1) 35 { //v是未匹配点 36 dy[v] = dx[u] + 1; 37 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 38 else 39 { 40 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 41 Q.push(cy[v]); 42 } 43 } 44 } 45 return dis != INF; 46 } 47 48 bool DFS(int u) 49 { 50 for (int v = 1; v <= Ny; v++) 51 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 52 { 53 vis[v] = 1; 54 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 55 if (cy[v] == -1 || DFS(cy[v])) 56 { //是增广路径,更新匹配集 57 cy[v] = u; cx[u] = v; 58 return 1; 59 } 60 } 61 return 0; 62 } 63 64 int MaxMatch() 65 { 66 int res = 0; 67 memset(cx, -1, sizeof(cx)); 68 memset(cy, -1, sizeof(cy)); 69 while (searchP()) 70 { 71 memset(vis, 0, sizeof(vis)); 72 for (int i = 1; i <= Nx; i++) 73 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 74 } 75 return res; 76 } 77 78 int main() 79 { 80 while (~scanf("%d",&n)) 81 { 82 memset(mp, 0, sizeof(mp)); 83 int cnt = 0,cur,num; 84 char c; 85 for (int i = 1; i <= n; i++) 86 { 87 cin >> cur >> c >> c >> num >> c; 88 cur = cur + 1; 89 for (int j = 1; j <= num; j++) 90 { 91 int v; 92 cin >> v; 93 v = v + 1; 94 mp[cur][v] = true; 95 mp[v][cur] = true; 96 } 97 } 98 99 100 Nx = n, Ny = n; 101 int ans = MaxMatch(); 102 printf("%d ", ans / 2); 103 } 104 return 0; 105 }
9、hdu 1151/poj 1422 Air Raid
题意:有一有向无环图,如果在一个结点放士兵,那么其也能够守卫与其相连的有向边能到达的结点。问最少需要多少守卫能够守卫所有结点。
思路:同上一题,不过注意有向边。
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, totalr, totalc; 8 const int maxn = 50;//最大行数 9 const int maxm = 15;//最大列数 10 const int maxk = 125;//x集与y集最多元素 11 const int INF = 0x7fffffff; 12 bool mp[maxk][maxk];//1表示该ij可以匹配 13 int cx[maxk];//记录x集合中匹配的y元素是哪一个 14 int dx[maxk]; 15 int cy[maxk];//记录y集合中匹配的x元素是哪一个 16 int dy[maxk]; 17 int vis[maxk];//标记该顶点是否访问过 18 bool searchP(void) //BFS 19 { 20 queue <int> Q; 21 dis = INF; 22 memset(dx, -1, sizeof(dx)); 23 memset(dy, -1, sizeof(dy)); 24 for (int i = 1; i <= Nx; i++) 25 if (cx[i] == -1) 26 { 27 Q.push(i); dx[i] = 0; 28 } 29 while (!Q.empty()) 30 { 31 int u = Q.front(); Q.pop(); 32 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 33 for (int v = 1; v <= Ny; v++) 34 if (mp[u][v] && dy[v] == -1) 35 { //v是未匹配点 36 dy[v] = dx[u] + 1; 37 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 38 else 39 { 40 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 41 Q.push(cy[v]); 42 } 43 } 44 } 45 return dis != INF; 46 } 47 48 bool DFS(int u) 49 { 50 for (int v = 1; v <= Ny; v++) 51 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 52 { 53 vis[v] = 1; 54 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 55 if (cy[v] == -1 || DFS(cy[v])) 56 { //是增广路径,更新匹配集 57 cy[v] = u; cx[u] = v; 58 return 1; 59 } 60 } 61 return 0; 62 } 63 64 int MaxMatch() 65 { 66 int res = 0; 67 memset(cx, -1, sizeof(cx)); 68 memset(cy, -1, sizeof(cy)); 69 while (searchP()) 70 { 71 memset(vis, 0, sizeof(vis)); 72 for (int i = 1; i <= Nx; i++) 73 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 74 } 75 return res; 76 } 77 78 int main() 79 { 80 int C; 81 scanf("%d", &C); 82 while (C--) 83 { 84 scanf("%d%d", &n, &m); 85 memset(mp, 0, sizeof(mp)); 86 for (int i = 1; i <= m; i++) 87 { 88 int u, v; 89 scanf("%d%d", &u, &v); 90 mp[u][v] = true; 91 } 92 Nx = n, Ny = n; 93 int ans = MaxMatch(); 94 printf("%d ", n-ans); 95 } 96 return 0; 97 }
10、poj 2594 Treasure Exploration(最小路径覆盖,可重点)
题意:同上一题近似,不过允许两个机器人搜索同一个顶点。
思路:用Folyd,把两个间接相连的顶点直接连上。之后思路和上一题类似。
1 //题意:选出最小路径覆盖图中所有点,路径可以交叉,也就是允许路径有重复的点。 2 #include<iostream> 3 #include<queue> 4 #include<memory.h> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 int n, t, m, Nx, Ny, dis, totalr, totalc; 9 const int maxn = 50;//最大行数 10 const int maxm = 15;//最大列数 11 const int maxk = 550;//x集与y集最多元素 12 const int INF = 0x7fffffff; 13 bool mp[maxk][maxk];//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 bool searchP(void) //BFS 20 { 21 queue <int> Q; 22 dis = INF; 23 memset(dx, -1, sizeof(dx)); 24 memset(dy, -1, sizeof(dy)); 25 for (int i = 1; i <= Nx; i++) 26 if (cx[i] == -1) 27 { 28 Q.push(i); dx[i] = 0; 29 } 30 while (!Q.empty()) 31 { 32 int u = Q.front(); Q.pop(); 33 if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充 34 for (int v = 1; v <= Ny; v++) 35 if (mp[u][v] && dy[v] == -1) 36 { //v是未匹配点 37 dy[v] = dx[u] + 1; 38 if (cy[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次 39 else 40 { 41 dx[cy[v]] = dy[v] + 1; //v是匹配点,继续延伸 42 Q.push(cy[v]); 43 } 44 } 45 } 46 return dis != INF; 47 } 48 49 bool DFS(int u) 50 { 51 for (int v = 1; v <= Ny; v++) 52 if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1) 53 { 54 vis[v] = 1; 55 if (cy[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。 56 if (cy[v] == -1 || DFS(cy[v])) 57 { //是增广路径,更新匹配集 58 cy[v] = u; cx[u] = v; 59 return 1; 60 } 61 } 62 return 0; 63 } 64 65 int MaxMatch() 66 { 67 int res = 0; 68 memset(cx, -1, sizeof(cx)); 69 memset(cy, -1, sizeof(cy)); 70 while (searchP()) 71 { 72 memset(vis, 0, sizeof(vis)); 73 for (int i = 1; i <= Nx; i++) 74 if (cx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++ 75 } 76 return res; 77 } 78 79 int main() 80 { 81 while (~scanf("%d%d", &n, &m)) 82 { 83 if (n == 0 && m == 0)break; 84 memset(mp, 0, sizeof(mp)); 85 for (int i = 1; i <= m; i++) 86 { 87 int u, v; 88 scanf("%d%d", &u, &v); 89 mp[u][v] = true; 90 } 91 //解决有重复点的问题~方法就是使用Floyd求闭包,就是把间接相连的点直接连上边,然后就是求最小路径覆盖 92 for (int k = 1; k <= n; k++) 93 { 94 for (int i = 1; i <= n; i++) 95 { 96 for (int j = 1; j <= n; j++) 97 { 98 if (!mp[i][j] && mp[i][k] && mp[k][j]) 99 { 100 mp[i][j] = true; 101 } 102 } 103 } 104 } 105 Nx = n, Ny = n; 106 int ans = MaxMatch(); 107 printf("%d ", n - ans); 108 } 109 return 0; 110 }
11、hdu 3829 Cat VS Dog (二分匹配、最大独立集)
题意:每个小孩都有喜欢的和不喜欢的某只狗或某只猫(如果喜欢狗,则不喜欢猫,反之亦然)。现在,动物园管理者想要淘汰一些狗和猫,使得最后高兴的孩子最多(只有喜欢的那只,不喜欢的那只被淘汰)。
思路:如果一个人喜欢的某只狗或某只猫恰好是另一个人不喜欢的某只狗或某只猫,则两者存在矛盾,将矛盾的两个人连线。最后求出的最大匹配数位=为要淘汰的动物的数目,剩下的人之间都没有互相矛盾的地方。即求最大独立集。
1 //题意:选出最小路径覆盖图中所有点,路径可以交叉,也就是允许路径有重复的点。 2 #include<iostream> 3 #include<queue> 4 #include<memory.h> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 int n, t, m,p, Nx, Ny, dis, totalr, totalc; 9 const int maxn = 110;//最大行数 10 const int maxm = 110;//最大列数 11 const int maxp = 550;//x集与y集最多元素 12 const int INF = 0x7fffffff; 13 bool mp[maxp][maxp];//1表示该ij可以匹配 14 int cx[maxp];//记录x集合中匹配的y元素是哪一个 15 int dx[maxp]; 16 int cy[maxp];//记录y集合中匹配的x元素是哪一个 17 int dy[maxp]; 18 int vis[maxp];//标记该顶点是否访问过 19 20 struct ps 21 { 22 int like; 23 int u; 24 int dislike; 25 int v; 26 }people[maxp]; 27 28 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() 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 //如果A喜欢的动物是B不喜欢的动物,或者A不喜欢的动物是B喜欢的动物,那么A、B之间就产生了矛盾,我们就在A和B之间建立一条边,然后求出最多有多少对孩子之间产生矛盾,用这个结果除以2就是最大匹配数。 90 int main() 91 { 92 while (~scanf("%d%d%d", &n, &m,&p)) 93 { 94 memset(mp, 0, sizeof(mp)); 95 96 for (int i = 1; i <= p; i++) 97 { 98 int u, v; 99 char like, dislike; 100 cin >> like >> u >> dislike >> v; 101 if (like == 'C') 102 { 103 people[i].like = 1; 104 people[i].u = u; 105 people[i].dislike = 2; 106 people[i].v = v; 107 } 108 else 109 { 110 people[i].like = 2; 111 people[i].u = u; 112 people[i].dislike = 1; 113 people[i].v = v; 114 } 115 116 } 117 for (int i = 1; i <= p; i++) 118 { 119 for (int j = i + 1; j <= p; j++) 120 { 121 ps a = people[i], b = people[j]; 122 if (a.dislike == b.like&&a.v == b.u) 123 { 124 mp[i][j] = mp[j][i] = true; 125 } 126 else if (a.like == b.dislike&&a.u == b.v) 127 { 128 mp[i][j] = mp[j][i] = true; 129 } 130 } 131 } 132 Nx = p, Ny = p; 133 int ans = MaxMatch(); 134 printf("%d ",p - ans/2); 135 } 136 return 0; 137 }
12、poj 2289/hdu 1669 Jamie's Contact Groups(二分图多重匹配)
题意:有n个人,有m个群组。每个人都有若干可以加入的群组。现在需要把每个人分配到一个群组,并且分配结束后,保证人数最多的那个群组的人数最少。
思路:①转换网络流。建立一个额外源点和汇点。限制每个群组的最大容量,源点与每个人相连,每条边容量为1;人和其能够加入的群组连边,容量为1;每个群组和汇点连边,容量为当前所枚举的限制容量。如果在当前限制容量下,最大流为n,则继续尝试缩小限制,否则放大限制。(二分操作)(Dinic算法,借鉴模板)
1 //网络流二分图多重匹配 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<map> 7 #include<sstream> 8 #include<vector> 9 #include<string> 10 using namespace std; 11 12 int n, m; 13 vector<int>pp[1010]; 14 15 const int INF = 250000; 16 /**oo 表示无穷大*/ 17 const int maxe = 1000010; 18 /**mm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/ 19 const int maxn = 1600; 20 /**mn 表示点的最大数量*/ 21 int nodes, st, ed, edges; 22 /**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/ 23 struct node 24 { 25 int from; 26 int to;//边指向的节点 27 int next;//链表的下一条边 28 int cap;//边的容量 29 int flow;//边的流量 30 }Eg[maxe]; 31 32 int head[maxn], work[maxn], level[maxn]; 33 /**head 节点的链表头,work 用于算法中的临时链表头,level 计算层次距离*/ 34 bool vis[maxn]; 35 36 /**初始化链表及图的信息*/ 37 void Init(int _node, int _src, int _dest) 38 { 39 nodes = _node, st = _src, ed = _dest; 40 memset(head, -1, sizeof(head)); 41 edges = 0; 42 } 43 /**增加一条u 到v 容量为c 的边*/ 44 void addedge(int u, int v, int c) 45 { 46 Eg[edges].flow = u, Eg[edges].to = v, Eg[edges].cap = c, Eg[edges].flow = 0, Eg[edges].next = head[u], head[u] = edges++; 47 Eg[edges].from = v, Eg[edges].to = u, Eg[edges].cap = 0, Eg[edges].flow = 0, Eg[edges].next = head[v], head[v] = edges++; 48 } 49 /**广搜计算出每个点与源点的最短距离,如果不能到达汇点说明算法结束*/ 50 void BuildGraph(int mid) 51 { 52 Init(n + m + 2, 0, n + m + 1); 53 for (int i = 1; i <= n; i++) addedge(st, i, 1); 54 for (int i = n + 1; i <= n + m; i++) addedge(i, ed, mid); 55 for (int i = 1; i <= n; i++) 56 { 57 int sz = pp[i].size(); 58 for (int j = 0; j < sz; j++) 59 { 60 addedge(i, pp[i][j], 1); 61 } 62 } 63 } 64 bool Dinic_bfs() 65 { 66 queue<int>q; 67 int i, u, v; 68 memset(level, -1, sizeof(level)); 69 memset(vis, 0, sizeof(vis)); 70 q.push(st); 71 level[st] = 0; 72 vis[st] = true; 73 while (!q.empty()) 74 { 75 u = q.front(); 76 q.pop(); 77 for (i = head[u]; i != -1; i = Eg[i].next) 78 { 79 v = Eg[i].to; 80 if (Eg[i].cap>Eg[i].flow && !vis[v]) 81 { 82 vis[v] = true; 83 /**这条边必须有剩余容量*/ 84 level[v] = level[u] + 1; 85 if (v == ed)return true; 86 q.push(v); 87 } 88 } 89 } 90 return false; 91 } 92 /**寻找可行流的增广路算法,按节点的距离来找,加快速度*/ 93 int Dinic_dfs(int u, int maxf) 94 { 95 if (u == ed || maxf == 0)return maxf; 96 /**work 是临时链表头,这里用i 引用它,这样寻找过的边不再寻找*/ 97 int flow = 0, f; 98 for (int &i = work[u], v; i != -1; i = Eg[i].next) 99 { 100 v = Eg[i].to; 101 if (level[v] == level[u] + 1 && (f = Dinic_dfs(v, min(maxf, Eg[i].cap - Eg[i].flow))) > 0) 102 { 103 Eg[i].flow += f; 104 Eg[i ^ 1].flow -= f; 105 /**正反向边容量改变*/ 106 flow += f; 107 maxf -= f; 108 if (maxf == 0) break; 109 } 110 } 111 return flow; 112 } 113 int Dinic_flow() 114 { 115 int l = 0, r = 1010,ans,ret; 116 while (l <= r) 117 { 118 int mid = (l + r) / 2; 119 ret = 0; 120 BuildGraph(mid); 121 while (Dinic_bfs()) 122 { 123 memcpy(work, head, sizeof(head)); 124 ret += Dinic_dfs(st, INF); 125 } 126 if (ret == n) 127 { 128 ans = mid; 129 r = mid - 1; 130 } 131 else 132 { 133 l = mid + 1; 134 } 135 } 136 return ans; 137 } 138 int main() 139 { 140 char name[20]; 141 string s; 142 while (~scanf("%d%d", &n, &m)) 143 { 144 if (n == 0 && m == 0)break; 145 for (int i = 1; i <= n; i++) pp[i].clear(); 146 for (int i = 1; i <= n; i++) 147 { 148 scanf("%s", name); 149 getline(cin, s); 150 stringstream sin(s); 151 int x; 152 while (sin >> x) 153 { 154 pp[i].push_back(x + 1 + n); 155 } 156 } 157 int ans = Dinic_flow(); 158 printf("%d ", ans); 159 } 160 return 0; 161 }
13、poj 2112 Optimal Milking(二分图多重匹配)
题意:给出牛和机器间的距离(二维矩阵表示,如果i==j,值为0;如果不能到达,值也输入为0),现在,在保证所有牛都能到达挤奶机器下,使得走得最长的牛的走的距离最小。
思路:①先用Floyd算出两两间最短距离。然后转换网络流,限制最大距离。建立一个额外源点与汇点,源点和每头牛相连,容量为1;牛和机器间的最短距离小于等于限制距离则连线,容量为1;机器和汇点连线,容量为1.每次二分限制最短距离。(Dinic算法,和上一个有所修改,上一个错误,不太明白)
1 //网络流二分图多重匹配 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<map> 7 #include<sstream> 8 #include<vector> 9 #include<string> 10 using namespace std; 11 12 int k, c, m; 13 14 15 const int INF = 0x3f3f3f3f; 16 /**oo 表示无穷大*/ 17 const int maxe = 200000; 18 /**mm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/ 19 const int maxn = 300; 20 /**mn 表示点的最大数量*/ 21 int nodes, st, ed, edges; 22 /**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/ 23 int mp[500][500]; 24 struct node 25 { 26 int from; 27 int to;//边指向的节点 28 int next;//链表的下一条边 29 int cap;//边的容量 30 int flow;//边的流量 31 }Eg[1000000]; 32 33 int head[15500], work[15500], level[15500]; 34 /**head 节点的链表头,work 用于算法中的临时链表头,level 计算层次距离*/ 35 bool vis[15500]; 36 37 /**初始化链表及图的信息*/ 38 void Init(int _node, int _src, int _dest) 39 { 40 nodes = _node, st = _src, ed = _dest; 41 memset(head, -1, sizeof(head)); 42 edges = 0; 43 } 44 /**增加一条u 到v 容量为c 的边*/ 45 void addedge(int u, int v, int c) 46 { 47 Eg[edges].flow = u, Eg[edges].to = v, Eg[edges].cap = c, Eg[edges].flow = 0, Eg[edges].next = head[u], head[u] = edges++; 48 Eg[edges].from = v, Eg[edges].to = u, Eg[edges].cap = 0, Eg[edges].flow = 0, Eg[edges].next = head[v], head[v] = edges++; 49 } 50 /**广搜计算出每个点与源点的最短距离,如果不能到达汇点说明算法结束*/ 51 void BuildGraph(int mid) 52 { 53 Init(k+c+2, 0, k + c + 1); 54 for (int i = 1; i <= k; i++) addedge(i, ed, m); 55 for (int i = k + 1; i <=k+c; i++) addedge(st,i, 1); 56 for (int i =k+1; i <= k+c; i++) 57 { 58 for (int j = 1; j <=k; j++) 59 { 60 if(mp[i][j]<=mid)addedge(i,j, 1); 61 } 62 } 63 } 64 bool Dinic_bfs() 65 { 66 queue<int>q; 67 int i, u, v; 68 memset(level, -1, sizeof(level)); 69 memset(vis, 0, sizeof(vis)); 70 q.push(st); 71 level[st] = 0; 72 vis[st] = true; 73 while (!q.empty()) 74 { 75 u = q.front(); 76 if (u == ed) return true; 77 q.pop(); 78 for (i = head[u]; i != -1; i = Eg[i].next) 79 { 80 v = Eg[i].to; 81 if (Eg[i].cap>Eg[i].flow && !vis[v]&&level[v]==-1) 82 { 83 vis[v] = true; 84 /**这条边必须有剩余容量*/ 85 level[v] = level[u] + 1; 86 q.push(v); 87 } 88 } 89 } 90 return false; 91 } 92 /**寻找可行流的增广路算法,按节点的距离来找,加快速度*/ 93 int Dinic_dfs(int u, int maxf) 94 { 95 if (u == ed)return maxf; 96 /**work 是临时链表头,这里用i 引用它,这样寻找过的边不再寻找*/ 97 int flow = 0, f; 98 for (int &i = work[u], v; i != -1; i = Eg[i].next) 99 { 100 v = Eg[i].to; 101 if (Eg[i].cap - Eg[i].flow>0&&level[v] == level[u] + 1) 102 { 103 f = Dinic_dfs(v, min(maxf, Eg[i].cap - Eg[i].flow)); 104 Eg[i].flow += f; 105 Eg[i ^ 1].flow -= f; 106 /**正反向边容量改变*/ 107 flow += f; 108 if (flow == maxf) return flow; 109 } 110 } 111 return flow; 112 } 113 int Dinic_flow() 114 { 115 int l = 0, r = 10000, ans, ret; 116 while (l <= r) 117 { 118 int mid = (l + r) / 2; 119 ret = 0; 120 BuildGraph(mid); 121 while (Dinic_bfs()) 122 { 123 memcpy(work, head, sizeof(head)); 124 ret += Dinic_dfs(st, INF); 125 } 126 if (ret == c) 127 { 128 ans = mid; 129 r = mid - 1; 130 } 131 else 132 { 133 l = mid + 1; 134 } 135 } 136 return ans; 137 } 138 int main() 139 { 140 while (~scanf("%d%d%d", &k, &c, &m)) 141 { 142 for (int i = 1; i <= k + c; i++) 143 { 144 for (int j = 1; j <= k + c; j++) 145 { 146 scanf("%d", &mp[i][j]); 147 if (i!=j&&mp[i][j] == 0) mp[i][j] = INF; 148 } 149 } 150 for (int z = 1; z <= k + c; z++) 151 { 152 for (int i = 1; i <= k + c; i++) 153 { 154 for (int j = 1; j <= k + c; j++) 155 { 156 mp[i][j] = min(mp[i][j], mp[i][z] + mp[z][j]); 157 } 158 } 159 } 160 int ans = Dinic_flow(); 161 printf("%d ", ans); 162 } 163 164 return 0; 165 }
14、poj 3189 Steady Cow Assignment
题意:有n头牛,b个畜棚。给出每头牛按照喜欢程度的优先级从大到小的牛棚编号,再给出每个牛棚的容量。求某一种分配下,在所有牛中,其分到的牛棚的最大优先级和最低优先级之差最小。
思路:①转换网络流,限制最大优先级和优先级之差,对于每一个限制,枚举其可能的范围(比如有4个牛棚,限制差值为2,则可能的优先级范围为[1,3]或[2,4])。建立额外的源点和汇点,源点和每头牛相连,容量为1;牛与其所划分优先级的范围内的牛棚连线,容量为1;每个牛棚和汇点相连,容量为牛棚各自的最大容纳牛的数目。求最大流。
1 #include<stdio.h> 2 #include<queue> 3 #include<string.h> 4 #include<iostream> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 const int maxe = 300000; 8 const int maxn = 2000; 9 const int maxb = 100; 10 const int maxnodes = 2000; 11 int head[maxnodes]; 12 int level[maxnodes]; 13 int cur[maxnodes]; 14 15 struct node 16 { 17 int from; 18 int to; 19 int w; 20 int next; 21 }e[maxe]; 22 int map[maxn][maxb]; 23 int barns[maxb]; 24 int b, n, st, ed, cont; 25 void addeg(int from, int to, int w) 26 { 27 e[cont].from = from; 28 e[cont].to = to; 29 e[cont].w = w; 30 e[cont].next = head[from]; 31 head[from] = cont++; 32 e[cont].from = to;//反向建边 33 e[cont].to = from; 34 e[cont].w = 0; 35 e[cont].next = head[to]; 36 head[to] = cont++; 37 } 38 void getmap(int x, int y) 39 { 40 st = 0; 41 ed = n + b + 1; 42 cont = 0; 43 memset(head, -1, sizeof(head)); 44 for (int i = 1; i <= n; i++) addeg(st, i, 1); 45 for (int i = n + 1; i <= n + b; i++) addeg(i, ed, barns[i - n]); 46 for (int i = 1; i <= n; i++) 47 { 48 for (int j = x; j <= y; j++) 49 { 50 addeg(i, n + map[i][j], 1); 51 } 52 } 53 } 54 int BFS() 55 { 56 memset(level, 0, sizeof(level)); 57 queue<int>s; 58 s.push(st); 59 level[st] = 1; 60 while (!s.empty()) 61 { 62 int u = s.front(); 63 if (u == ed)return 1; 64 s.pop(); 65 for (int i = head[u]; i != -1; i = e[i].next) 66 { 67 int v = e[i].to; 68 int w = e[i].w; 69 if (w&&level[v] == 0) 70 { 71 level[v] = level[u] + 1; 72 s.push(v); 73 } 74 } 75 } 76 return 0; 77 } 78 int Dfs(int u, int maxflow, int tt) 79 { 80 if (u == tt)return maxflow; 81 int ret = 0; 82 for (int &i = cur[u]; i != -1; i = e[i].next) 83 { 84 int v = e[i].to; 85 int w = e[i].w; 86 if (w&&level[v] == level[u] + 1) 87 { 88 int f = Dfs(v, min(maxflow - ret, w), tt); 89 e[i].w -= f; 90 e[i ^ 1].w += f; 91 ret += f; 92 if (ret == maxflow)return ret; 93 } 94 } 95 return ret; 96 } 97 int Dinic(int x, int y) 98 { 99 getmap(x, y); 100 int ans = 0; 101 while (BFS()) 102 { 103 memcpy(cur, head, sizeof(head)); 104 ans += Dfs(st, INF, ed); 105 } 106 if (ans == n)return 1; 107 else return 0; 108 } 109 int main() 110 { 111 while (~scanf("%d%d", &n, &b)) 112 { 113 for (int i = 1; i <= n; i++) 114 { 115 for (int j = 1; j <= b; j++) 116 { 117 scanf("%d", &map[i][j]); 118 } 119 } 120 for (int i = 1; i <= b; i++) scanf("%d", &barns[i]); 121 int ans = INF; 122 bool flag = true; 123 for (int len = 1; flag&& len <= b; len++) 124 { 125 for (int i = 1; flag&&i + len - 1 <= b; i++) 126 { 127 if (Dinic(i, i + len - 1)) 128 { 129 ans = len; 130 flag = false; 131 break; 132 } 133 } 134 } 135 printf("%d ", ans); 136 } 137 }
15、poj 3436 ACM Computer Factory
题意:我们需要装配p个配件。每台机器都有自己的限制(满足进入限制条件(不能有哪些零件0、必须要有哪些零件1、哪些零件可有可无2)后才能加工该半成品,加工后则会有些零件具备1,有些零件没具备0)。同时,每台机器有各自的效率。问如何设置加工流使得最后总效率最大。
思路:最大流。建立源点(什么零件都没有)和汇点(什么零件都有)。同时把每台机器分成两个点,一个输入,一个输出。只有满足输出输入关系的不同机器才能连线,容量为INF。对于同一台机器,从输入连到输出,容量为效率。源点和满足输入条件的机器相连,容量为INF;机器输出后满足汇点要求的输出口和汇点相连,容量为INF,求一次最大流。(Edmonds_Karp算法模板)
1 #include<iostream> 2 #include<memory.h> 3 #include<queue> 4 using namespace std; 5 int p, n,st,ed; 6 const int maxn = 55; 7 const int maxp = 15; 8 const int maxe = 10000; 9 const int INF = 0x7fffffff; 10 11 struct line 12 { 13 int u; 14 int v; 15 int f; 16 }lines[maxe]; 17 18 int in[maxn][maxp];//记录每台机器的输入要求 19 int out[maxn][maxp];//记录每台机器的输出情况 20 int k[maxn];//记录每台机器的最大组装效率 21 22 int mp[maxn * 2][maxn * 2];//网络图 23 int mptmp[maxn * 2][maxn * 2];//初始图备份 24 25 int prepath[maxn * 2]; 26 int flow[maxn * 2]; 27 bool vis[maxn * 2]; 28 int BFS() 29 { 30 queue<int>q; 31 memset(prepath, 0, sizeof(prepath)); 32 memset(vis, 0, sizeof(vis)); 33 prepath[st] = st; 34 flow[st] = INF; 35 q.push(st); 36 vis[st] = true; 37 while (!q.empty()) 38 {//可以用优先队列优化 39 int t = q.front(); 40 q.pop(); 41 if (t == ed)break; 42 for (int i = 1; i <= n*2+1; i++) 43 { 44 if (i != st&&!vis[i]&& mp[t][i]) 45 { 46 vis[i] = true;//防止走反向 47 flow[i] = flow[t] < mp[t][i] ? flow[t] : mp[t][i]; 48 q.push(i); 49 prepath[i] = t; 50 } 51 } 52 } 53 if (prepath[ed] == 0) return -1; 54 else return flow[ed]; 55 } 56 57 int Edmonds_Karp() 58 { 59 int maxflow = 0, dflow, cur, pre; 60 while (1) 61 { 62 dflow = BFS(); 63 if (dflow == -1)break; 64 maxflow += dflow; 65 cur = ed; 66 while (cur != st) 67 { 68 pre = prepath[cur]; 69 mp[pre][cur] -= dflow;//更新正向边 70 mp[cur][pre] += dflow;//添加反向边 71 cur = pre; 72 } 73 } 74 return maxflow; 75 } 76 77 int main() 78 { 79 while (~scanf("%d%d", &p, &n)) 80 { 81 memset(mp, 0, sizeof(mp)); 82 st = 0, ed = 2 * n + 1; 83 for (int i = 1; i <= n; i++) 84 { 85 scanf("%d", &k[i]); 86 for (int j = 1; j <= p; j++) 87 { 88 scanf("%d", &in[i][j]); 89 } 90 for (int j = 1; j <= p; j++) 91 { 92 scanf("%d", &out[i][j]); 93 } 94 mp[i][i + n] = k[i]; 95 } 96 for (int i = 1; i <= n; i++) 97 { 98 for (int j = 1; j <= n; j++) 99 { 100 if (i == j) continue; 101 bool flag = true; 102 for (int k = 1; k <= p; k++) 103 { 104 if (out[i][k] + in[j][k] == 1) 105 { 106 flag = false; 107 break; 108 } 109 } 110 if (flag) mp[i + n][j] = INF; 111 } 112 } 113 for (int i = 1; i <= n; i++) 114 { 115 bool flag = true; 116 for (int j = 1; j <= p; j++) 117 { 118 if (in[i][j] == 1) 119 { 120 flag = false; 121 break; 122 } 123 } 124 if (flag)mp[st][i] = INF; 125 } 126 for (int i = 1; i <= n; i++) 127 { 128 bool flag = true; 129 for (int j = 1; j <= p; j++) 130 { 131 if (out[i][j] == 0) 132 { 133 flag = false; 134 break; 135 } 136 } 137 if (flag) mp[i + n][ed] = INF; 138 } 139 memcpy(mptmp, mp, sizeof(mp)); 140 int ans = Edmonds_Karp(); 141 int totaline = 0; 142 for (int i = 1; i <= n; i++) 143 { 144 for (int j = 1; j <= n; j++) 145 { 146 if (mptmp[i + n][j] > mp[i + n][j]) 147 { 148 lines[totaline].u = i; 149 lines[totaline].v = j; 150 lines[totaline].f = mptmp[i + n][j] - mp[i + n][j]; 151 totaline++; 152 } 153 } 154 } 155 printf("%d %d ", ans, totaline); 156 for (int i = 0; i < totaline; i++) 157 { 158 printf("%d %d %d ", lines[i].u, lines[i].v, lines[i].f); 159 } 160 } 161 return 0; 162 }
16、poj 3281 Dining
题意:每头牛都有自己喜欢的若干个食物(编号)和喜欢的若干个饮料(编号)。但是每种食物或每种饮料都只能给一头牛。求饮料和食物都能够得到的牛的最大个数。
思路:最大流。建立源点和汇点。源点和每种食物相连,容量为1;把每头牛拆成两个点(一个连接食物<左牛>,一个连接饮料<右牛>),同一头牛的两个点之间连线,容量为1;把食物和喜欢该食物的那头牛的左牛连线,容量为1;把右牛和该牛喜欢的饮料连线,容量为1;把所有饮料连向汇点,容量为1.求一次最大流。(Edmonds_Karp算法模板)
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 int n, f, d,st,ed; 6 const int maxn = 450; 7 const int maxe = 250000; 8 const int INF = 0x7fffffff; 9 10 int mp[maxn][maxn]; 11 int level[maxn];//分层 12 int vis[maxn]; 13 int flow[maxn]; 14 int prepath[maxn]; 15 16 int BFS() 17 { 18 queue<int>q; 19 memset(prepath, 0, sizeof(prepath)); 20 memset(vis, 0, sizeof(vis)); 21 prepath[st] = st; 22 flow[st] = INF; 23 q.push(st); 24 vis[st] = true; 25 while (!q.empty()) 26 {//可以用优先队列优化 27 int t = q.front(); 28 q.pop(); 29 if (t == ed)break; 30 for (int i = 1; i <=ed; i++) 31 { 32 if (i != st && !vis[i] && mp[t][i]) 33 { 34 vis[i] = true;//防止走反向 35 flow[i] = flow[t] < mp[t][i] ? flow[t] : mp[t][i]; 36 q.push(i); 37 prepath[i] = t; 38 } 39 } 40 } 41 if (prepath[ed] == 0) return -1; 42 else return flow[ed]; 43 } 44 45 int Edmonds_Karp() 46 { 47 int maxflow = 0, dflow, cur, pre; 48 while (1) 49 { 50 dflow = BFS(); 51 if (dflow == -1)break; 52 maxflow += dflow; 53 cur = ed; 54 while (cur != st) 55 { 56 pre = prepath[cur]; 57 mp[pre][cur] -= dflow;//更新正向边 58 mp[cur][pre] += dflow;//添加反向边 59 cur = pre; 60 } 61 } 62 return maxflow; 63 } 64 65 66 int main() 67 { 68 while (~scanf("%d%d%d", &n, &f, &d)) 69 { 70 //建图 71 st = 0, ed = 2 * n + f + d + 1; 72 memset(mp, 0, sizeof(mp)); 73 for (int i = 1; i <= n; i++) 74 { 75 mp[i][i + n] = 1;//每头牛拆点,左牛到右牛 76 int fi, di; 77 scanf("%d%d", &fi, &di); 78 for (int j = 1; j <= fi; j++) 79 { 80 int ff; 81 scanf("%d", &ff); 82 mp[2 * n + ff][i] = 1;//食物到左牛 83 } 84 for (int j = 1; j <= di; j++) 85 { 86 int dd; 87 scanf("%d", &dd); 88 mp[i + n][2 * n + f + dd] = 1;//右牛到饮料 89 } 90 91 } 92 for (int i = 1; i <= f; i++) mp[0][2 * n + i] = 1;//源点到食物 93 for (int j = 1; j <= d; j++)mp[2 * n + f + j][2 * n + f + d + 1] = 1;//饮料到汇点 94 95 //Edmonds_Karp算法 96 int ans = Edmonds_Karp(); 97 98 printf("%d ", ans); 99 } 100 return 0; 101 }
17、poj 1273 Drainage Ditches
题意:有n个沟渠,m个结点(编号为1的为池塘,编号为m的为溪流)。每个沟渠都有自己的最大流速,问从池塘到溪流的最大排水流速。
思路:源点为池塘,汇点为溪流。根据沟渠的走向和流速建立图,求最大流。基础题。(Edmonds_Karp算法模板)
1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 5 const int INF = 0x7fffffff; 6 int V, E; 7 int level[205]; 8 int Si, Ei, Ci; 9 10 struct Dinic 11 { 12 int c; 13 int f; 14 }edge[205][205]; 15 16 bool dinic_bfs() //bfs方法构造层次网络 17 { 18 queue<int> q; 19 memset(level, 0, sizeof(level)); 20 q.push(1); 21 level[1] = 1; 22 int u, v; 23 while (!q.empty()) 24 { 25 u = q.front(); 26 q.pop(); 27 for (v = 1; v <= E; v++) 28 { 29 if (!level[v] && edge[u][v].c>edge[u][v].f) 30 { 31 level[v] = level[u] + 1; 32 q.push(v); 33 } 34 } 35 } 36 return level[E] != 0; //question: so it must let the sink node is the Mth?/the way of yj is give the sink node's id 37 } 38 39 int dinic_dfs(int u, int cp) 40 { //use dfs to augment the flow 41 int tmp = cp; 42 int v, t; 43 if (u == E) 44 return cp; 45 for (v = 1; v <= E&&tmp; v++) 46 { 47 if (level[u] + 1 == level[v]) 48 { 49 if (edge[u][v].c>edge[u][v].f) 50 { 51 t = dinic_dfs(v, min(tmp, edge[u][v].c - edge[u][v].f)); 52 edge[u][v].f += t; 53 edge[v][u].f -= t; 54 tmp -= t; 55 } 56 } 57 } 58 return cp - tmp; 59 } 60 int dinic() 61 { 62 int sum = 0, tf = 0; 63 while (dinic_bfs()) 64 { 65 while (tf = dinic_dfs(1, INF)) 66 sum += tf; 67 } 68 return sum; 69 } 70 71 int main() 72 { 73 while (scanf("%d%d", &V, &E)) 74 { 75 memset(edge, 0, sizeof(edge)); 76 while (V--) 77 { 78 scanf("%d%d%d", &Si, &Ei, &Ci); 79 edge[Si][Ei].c += Ci; 80 } 81 int ans = dinic(); 82 printf("%d ", ans); 83 } 84 return 0; 85 }
18、poj 1087/uva 753 A Plug for UNIX
题意:房间内有n个插座板,有m个设备,有k种接口转换器,求最少有多少设备不能插进插座。
思路:最大流。关键是建图。见代码注释。Dinic算法。
1 #include<iostream> 2 #include<queue> 3 #include<map> 4 #include<string> 5 using namespace std; 6 const int INF = 0x3f3f3f3f; 7 struct node 8 { 9 int from; 10 int to;//边指向的节点 11 int next;//链表的下一条边 12 int cap;//边的容量 13 int flow;//边的流量 14 }Eg[100000]; 15 16 int head[1000], work[1000], level[1000]; 17 /**head 节点的链表头,work 用于算法中的临时链表头,level 计算层次距离*/ 18 bool vis[15500]; 19 int edges, st, ed,nodes; 20 int n,m,k; 21 map<string, int>gid; 22 int rpt[110];//房间有哪些类型插座 23 int devices[110];//用电器插头类型 24 int adp[110][2];//转接器 25 /**初始化链表及图的信息*/ 26 void Init(int _node, int _src, int _dest) 27 { 28 nodes = _node, st = _src, ed = _dest; 29 memset(head, -1, sizeof(head)); 30 edges = 0; 31 } 32 /**增加一条u 到v 容量为c 的边*/ 33 void addedge(int u, int v, int c) 34 { 35 Eg[edges].from = u, Eg[edges].to = v, Eg[edges].cap = c, Eg[edges].flow = 0, Eg[edges].next = head[u], head[u] = edges++; 36 Eg[edges].from = v, Eg[edges].to = u, Eg[edges].cap = 0, Eg[edges].flow = 0, Eg[edges].next = head[v], head[v] = edges++; 37 } 38 /**广搜计算出每个点与源点的最短距离,如果不能到达汇点说明算法结束*/ 39 void BuildGraph(int cnt) 40 { 41 //源点为0,房间插座为1~n,用电器为n+1~n+m,转接器为n+m+1~n+m+k,汇点为n+m+k+1.从插座往插头连线 42 Init(n+m+k+1, 0,n+m+k+1); 43 //源点和插座连接 44 for (int i = 1; i <= n; i++) addedge(st, i, 1); 45 //用电器到汇点 46 for (int i = 1; i <= m; i++) addedge(n+i, ed, 1); 47 //插座到用电器(若可以直连) 48 for (int i = 1; i <= m; i++) 49 { 50 if (devices[i] <= n) addedge(devices[i], n + i, 1); 51 } 52 //插座到转换器插头 53 for (int i = 1; i <= k; i++) 54 { 55 if (adp[i][1] <= n) addedge(adp[i][1], n + m + i, INF); 56 } 57 //转换器插孔到转换器插头 58 for (int i = 1; i <= k; i++) 59 { 60 for (int j = 1; j <= k; j++) 61 { 62 if (i != j&&adp[i][0] == adp[j][1]) 63 { 64 addedge(n + m + i, n + m + j,INF); 65 } 66 } 67 } 68 //转换器插孔到用电器 69 for (int i = 1; i <= k; i++) 70 { 71 for (int j = 1; j <= m; j++) 72 { 73 if (adp[i][0] == devices[j]) 74 { 75 addedge(n + m + i, n + j, INF); 76 } 77 } 78 } 79 } 80 bool Dinic_bfs() 81 { 82 queue<int>q; 83 int i, u, v; 84 memset(level, -1, sizeof(level)); 85 memset(vis, 0, sizeof(vis)); 86 q.push(st); 87 level[st] = 0; 88 vis[st] = true; 89 while (!q.empty()) 90 { 91 u = q.front(); 92 if (u == ed) return true; 93 q.pop(); 94 for (i = head[u]; i != -1; i = Eg[i].next) 95 { 96 v = Eg[i].to; 97 if (Eg[i].cap>Eg[i].flow && !vis[v] && level[v] == -1) 98 { 99 vis[v] = true; 100 /**这条边必须有剩余容量*/ 101 level[v] = level[u] + 1; 102 q.push(v); 103 } 104 } 105 } 106 return false; 107 } 108 /**寻找可行流的增广路算法,按节点的距离来找,加快速度*/ 109 int Dinic_dfs(int u, int maxf) 110 { 111 if (u == ed)return maxf; 112 /**work 是临时链表头,这里用i 引用它,这样寻找过的边不再寻找*/ 113 int flow = 0, f; 114 for (int &i = work[u], v; i != -1; i = Eg[i].next) 115 { 116 v = Eg[i].to; 117 if (Eg[i].cap - Eg[i].flow>0 && level[v] == level[u] + 1) 118 { 119 f = Dinic_dfs(v, min(maxf, Eg[i].cap - Eg[i].flow)); 120 Eg[i].flow += f; 121 Eg[i ^ 1].flow -= f; 122 /**正反向边容量改变*/ 123 flow += f; 124 if (flow == maxf) return flow; 125 } 126 } 127 return flow; 128 } 129 int Dinic_flow() 130 { 131 int ret = 0; 132 while (Dinic_bfs()) 133 { 134 memcpy(work, head, sizeof(head)); 135 ret += Dinic_dfs(st, INF); 136 } 137 return ret; 138 } 139 int main() 140 { 141 string s; 142 while (~scanf("%d",&n)) 143 { 144 int cnt = 1; 145 for (int i = 1; i <= n; i++) 146 { 147 cin >> s; 148 if (!gid[s]) 149 { 150 gid[s] = cnt++; 151 } 152 } 153 n = cnt - 1; 154 //房间插座编号1~n 155 scanf("%d", &m); 156 for (int i = 1; i <= m; i++) 157 { 158 cin >> s; 159 cin >> s; 160 if (!gid[s]) gid[s] = cnt++; 161 int pos = gid[s]; 162 devices[i] = pos; 163 }//设备编号n+1~n+m 164 scanf("%d", &k); 165 for (int i = 1; i <= k; i++) 166 { 167 cin >> s; 168 if (!gid[s]) gid[s] = cnt++; 169 int ff = gid[s]; 170 cin >> s; 171 if (!gid[s]) gid[s] = cnt++; 172 int tt = gid[s]; 173 adp[i][0] = ff; 174 adp[i][1] = tt; 175 } 176 BuildGraph(cnt); 177 printf("%d ", m-Dinic_flow()); 178 } 179 return 0; 180 }