题目链接:https://vjudge.net/problem/URAL-1099
1099. Work Scheduling
Time limit: 0.5 second
Memory limit: 64 MB
Memory limit: 64 MB
There is a certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to be scheduled in pairs so that each pair guards in a different night. The junkyard CEO ordered you to write a program which given the guards characteristics determines the maximum amount of scheduled guards (the rest will be fired). Please note that each guard can be scheduled with only one of his colleagues and no guard can work alone.
Input
The first line of the input contains one number
N ≤ 222 which is a number of night guards. Unlimited number of lines consisting of unordered pairs (
i,
j) follow, each such pair means that guard #
i and guard #
j can work together, because it is possible to find uniforms that suit both of them (The junkyard uses different parts of uniforms for different guards i.e. helmets, pants, jackets. It is impossible to put small helmet on a guard with a big head or big shoes on guard with small feet). The input ends with Eof.
Output
You should output one possible optimal assignment. On the first line of the output write the even number
C, the amount of scheduled guards. Then output
C/2 lines, each containing 2 integers (
i,
j) that denote that
i and
j will work together.
Sample
input | output |
---|---|
3 1 2 2 3 1 3 | 2 1 2
|
题解:
一般图匹配带花树的模板题。还是看不太懂,以后有时间再看看。
有关怎么找到奇环:
其中可以把最右边两个点看成是找到奇环时的两个点,其中root为他们的LCA。
疑问1:什么要把花上的边的方向取反?
疑问2:为什么在找奇环时要特判 v==start ?
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <string> 6 #include <vector> 7 #include <map> 8 #include <set> 9 #include <queue> 10 #include <sstream> 11 #include <algorithm> 12 using namespace std; 13 const int INF = 2e9; 14 const int MOD = 1e9+7; 15 const int MAXM = 20+10; 16 const int MAXN = 2e3+10; 17 18 int N; 19 bool graph[MAXN][MAXN]; 20 int match[MAXN]; 21 bool inque[MAXN], inpath[MAXN], inblos[MAXN]; 22 queue<int>q; 23 int start, finish; 24 int newbase, fa[MAXN], base[MAXN]; 25 26 int FindCommonAncester(int u, int v) //仅仅是找LCA,并不修改任何值 27 { 28 memset(inpath, false, sizeof(inpath)); 29 while(true) 30 { 31 u = base[u]; 32 inpath[u] = true; 33 if(u==start) break; 34 u = fa[match[u]]; 35 } 36 while(true) 37 { 38 v = base[v]; 39 if(inpath[v]) break; 40 v = fa[match[v]]; 41 } 42 return v; 43 } 44 45 void ResetTrace(int u) //把fa的方向取反 46 { 47 int v; 48 while(base[u]!=newbase) //匹配边、非匹配边交替出现 49 { 50 v = match[u]; //u--match[u]匹配边 51 inblos[base[u]] = inblos[base[v]] = true; 52 u = fa[v]; //v--fa[v]非匹配边 53 if(base[u]!=newbase) fa[u] = v; 54 } 55 } 56 57 void BloosomContract(int u, int v) 58 { 59 newbase = FindCommonAncester(u, v); 60 memset(inblos, false, sizeof(inblos)); 61 ResetTrace(u); //把u到LCA上的边取反 62 ResetTrace(v); //把v到LCA上的边取反 63 if(base[u]!=newbase) fa[u] = v; //看不懂 64 if(base[v]!=newbase) fa[v] = u; 65 66 for(int tu = 1; tu<=N; tu++) 67 if(inblos[base[tu]]) 68 { 69 base[tu] = newbase; //设置它属于的集合 70 if(!inque[tu]) q.push(tu); //在花中的点加入队列,因为与花中点相连的点还可以找增广路 71 } 72 } 73 74 void FindAugmentingPath() 75 { 76 memset(inque, false, sizeof(inque)); 77 memset(fa, 0, sizeof(fa)); 78 for(int i = 1; i<=N; i++) 79 base[i] = i; 80 while(!q.empty()) q.pop(); 81 q.push(start); 82 finish = 0; 83 84 while(!q.empty()) 85 { 86 int u = q.front(); q.pop(); 87 88 for(int v = 1; v<=N; v++) //fa[u]--u是匹配边, u--v是未匹配边。 89 if(graph[u][v] && base[u]!=base[v] && match[u]!=v ) 90 { 91 //为什么要特判 v==start ? 92 if( (v==start) || (match[v]>0 && fa[match[v]]>0)) //找到奇环。 93 BloosomContract(u, v); 94 else if(fa[v]==0) //v点在这次找增广路时没有被访问 95 { 96 fa[v] = u; 97 if(match[v]>0) //如果已经匹配了,则加入他的匹配点,继续找增广路。 98 q.push(match[v]); 99 else //如果没有匹配,则找到了增广路。 100 { 101 finish = v; 102 return; 103 } 104 } 105 } 106 } 107 } 108 109 void AugmentPath() 110 { 111 int u, v, w; 112 u = finish; 113 while(u>0) //沿着增广路往回走,把匹配边和非匹配边取反 114 { 115 v = fa[u]; 116 w = match[v]; 117 match[v] = u; 118 match[u] = v; 119 u = w; 120 } 121 } 122 123 void Edmonds() 124 { 125 memset(match, 0, sizeof(match)); 126 for(int u = 1; u<=N; u++) 127 if(match[u]==0) 128 { 129 start = u; 130 FindAugmentingPath(); 131 if(finish>0) AugmentPath(); 132 } 133 } 134 135 int main() 136 { 137 scanf("%d", &N); 138 memset(graph, false, sizeof(graph)); 139 int u, v; 140 while(scanf("%d%d", &u, &v)!=EOF) 141 graph[u][v] = graph[v][u] = true; 142 143 Edmonds(); 144 int sum = 0; 145 for(int u = 1; u<=N; u++) 146 if(match[u]>0) sum++; 147 printf("%d\n", sum); 148 for(int u = 1; u<=N; u++) 149 if(u<match[u]) 150 printf("%d %d\n", u, match[u]); 151 152 return 0; 153 }