URAL1099 Work Scheduling —— 一般图匹配带花树

题目链接:https://vjudge.net/problem/URAL-1099

 

1099. Work Scheduling

Time limit: 0.5 second
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 ( ij) 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 ( ij) that denote that  i and  j will work together.

Sample

inputoutput
3
1 2
2 3
1 3
2
1 2

 

 

 

题解:

一般图匹配带花树的模板题。还是看不太懂,以后有时间再看看。

 

有关怎么找到奇环:

其中可以把最右边两个点看成是找到奇环时的两个点,其中root为他们的LCA。

疑问1:什么要把花上的边的方向取反?

疑问2:为什么在找奇环时要特判 v==start ?

 

 

代码如下:

  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 }
View Code

 

转载于:https://www.cnblogs.com/DOLFAMINGO/p/7842588.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值