URAL 1099 Work scheduling 一般图的最大匹配 带花树算法(模板)
Description
There is certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to scheduled in pairs, so that each pair guards at 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 the amount 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
input | output |
---|---|
3 1 2 2 3 1 3 |
2 1 2 |
就是有n个人,之后给出若干个关系,之后求最大可以保留多少对关系,要求可以删除若干个人
下面两个都是模板,两个模板都是可以套用的·1
1
#include<stdio.h> #include<string.h> #include<string.h> #include<iostream> #include<queue> #include<algorithm> using namespace std; const int MAXN = 250; int N; //点的个数,点的编号从1到N bool Graph[MAXN][MAXN]; int Match[MAXN]; bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN]; int Head,Tail; int Queue[MAXN]; int Start,Finish; int NewBase; int Father[MAXN],Base[MAXN]; int Count;//匹配数,匹配对数是Count/2 void CreateGraph() { int u,v; memset(Graph,false,sizeof(Graph)); scanf("%d",&N); while(scanf("%d%d",&u,&v) == 2) { Graph[u][v] = Graph[v][u] = true; } } void Push(int u) { Queue[Tail] = u; Tail++; InQueue[u] = true; } int Pop() { int res = Queue[Head]; Head++; return res; } int FindCommonAncestor(int u,int v) { memset(InPath,false,sizeof(InPath)); while(true) { u = Base[u]; InPath[u] = true; if(u == Start) break; u = Father[Match[u]]; } while(true) { v = Base[v]; if(InPath[v])break; v = Father[Match[v]]; } return v; } void ResetTrace(int u) { int v; while(Base[u] != NewBase) { v = Match[u]; InBlossom[Base[u]] = InBlossom[Base[v]] = true; u = Father[v]; if(Base[u] != NewBase) Father[u] = v; } } void BloosomContract(int u,int v) { NewBase = FindCommonAncestor(u,v); memset(InBlossom,false,sizeof(InBlossom)); ResetTrace(u); ResetTrace(v); if(Base[u] != NewBase) Father[u] = v; if(Base[v] != NewBase) Father[v] = u; for(int tu = 1; tu <= N; tu++) if(InBlossom[Base[tu]]) { Base[tu] = NewBase; if(!InQueue[tu]) Push(tu); } } void FindAugmentingPath() { memset(InQueue,false,sizeof(InQueue)); memset(Father,0,sizeof(Father)); for(int i = 1; i <= N; i++) Base[i] = i; Head = Tail = 1; Push(Start); Finish = 0; while(Head < Tail) { int u = Pop(); for(int v = 1; v <= N; v++) if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v)) { if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0)) BloosomContract(u,v); else if(Father[v] == 0) { Father[v] = u; if(Match[v] > 0) Push(Match[v]); else { Finish = v; return; } } } } } void AugmentPath() { int u,v,w; u = Finish; while(u > 0) { v = Father[u]; w = Match[v]; Match[v] = u; Match[u] = v; u = w; } } void Edmonds() { memset(Match,0,sizeof(Match)); for(int u = 1; u <= N; u++) if(Match[u] == 0) { Start = u; FindAugmentingPath(); if(Finish > 0)AugmentPath(); } } void PrintMatch() { Count = 0; for(int u = 1; u <= N; u++) if(Match[u] > 0) Count++; printf("%d\n",Count); for(int u = 1; u <= N; u++) if(u < Match[u]) printf("%d %d\n",u,Match[u]); } int main() { CreateGraph();//建图 Edmonds();//进行匹配 PrintMatch();//输出匹配数和匹配 return 0; }
(2)
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int N = 250; // 并查集维护 int belong[N]; int findb(int x) { return belong[x] == x ? x : belong[x] = findb(belong[x]); } void unit(int a, int b) { a = findb(a); b = findb(b); if (a != b) belong[a] = b; } int n, match[N]; vector<int> e[N]; int Q[N], rear; int next[N], mark[N], vis[N]; // 朴素算法求某阶段中搜索树上两点x, y的最近公共祖先r int LCA(int x, int y) { static int t = 0; t++; while (true) { if (x != -1) { x = findb(x); // 点要对应到对应的花上去 if (vis[x] == t) return x; vis[x] = t; if (match[x] != -1) x = next[match[x]]; else x = -1; } swap(x, y); } } void group(int a, int p) { while (a != p) { int b = match[a], c = next[b]; // next数组是用来标记花朵中的路径的,综合match数组来用,实际上形成了 // 双向链表,如(x, y)是匹配的,next[x]和next[y]就可以指两个方向了。 if (findb(c) != p) next[c] = b; // 奇环中的点都有机会向环外找到匹配,所以都要标记成S型点加到队列中去, // 因环内的匹配数已饱和,因此这些点最多只允许匹配成功一个点,在aug中 // 每次匹配到一个点就break终止了当前阶段的搜索,并且下阶段的标记是重 // 新来过的,这样做就是为了保证这一点。 if (mark[b] == 2) mark[Q[rear++] = b] = 1; if (mark[c] == 2) mark[Q[rear++] = c] = 1; unit(a, b); unit(b, c); a = c; } } // 增广 void aug(int s) { for (int i = 0; i < n; i++) // 每个阶段都要重新标记 next[i] = -1, belong[i] = i, mark[i] = 0, vis[i] = -1; mark[s] = 1; Q[0] = s; rear = 1; for (int front = 0; match[s] == -1 && front < rear; front++) { int x = Q[front]; // 队列Q中的点都是S型的 for (int i = 0; i < (int)e[x].size(); i++) { int y = e[x][i]; if (match[x] == y) continue; // x与y已匹配,忽略 if (findb(x) == findb(y)) continue; // x与y同在一朵花,忽略 if (mark[y] == 2) continue; // y是T型点,忽略 if (mark[y] == 1) { // y是S型点,奇环缩点 int r = LCA(x, y); // r为从i和j到s的路径上的第一个公共节点 if (findb(x) != r) next[x] = y; // r和x不在同一个花朵,next标记花朵内路径 if (findb(y) != r) next[y] = x; // r和y不在同一个花朵,next标记花朵内路径 // 将整个r -- x - y --- r的奇环缩成点,r作为这个环的标记节点,相当于论文中的超级节点 group(x, r); // 缩路径r --- x为点 group(y, r); // 缩路径r --- y为点 } else if (match[y] == -1) { // y自由,可以增广,R12规则处理 next[y] = x; for (int u = y; u != -1; ) { // 交叉链取反 int v = next[u]; int mv = match[v]; match[v] = u, match[u] = v; u = mv; } break; // 搜索成功,退出循环将进入下一阶段 } else { // 当前搜索的交叉链+y+match[y]形成新的交叉链,将match[y]加入队列作为待搜节点 next[y] = x; mark[Q[rear++] = match[y]] = 1; // match[y]也是S型的 mark[y] = 2; // y标记成T型 } } } } bool g[N][N]; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) g[i][j] = false; // 建图,双向边 int x, y; while (scanf("%d%d", &x, &y) != EOF) { x--, y--; if (x != y && !g[x][y]) e[x].push_back(y), e[y].push_back(x); g[x][y] = g[y][x] = true; } // 增广匹配 for (int i = 0; i < n; i++) match[i] = -1; for (int i = 0; i < n; i++) if (match[i] == -1) aug(i); // 输出答案 int tot = 0; for (int i = 0; i < n; i++) if (match[i] != -1) tot++; printf("%d\n", tot); for (int i = 0; i < n; i++) if (match[i] > i) printf("%d %d\n", i + 1, match[i] + 1); return 0; }