poj2942 Knights of the Round Table(点双联通分量+染色法)
题目大意
有n个骑士,m对憎恨关系,每个憎恨的骑士不会坐在一起。
现在要将尽量多的奇数个人安排到一个圆桌上参加会议,将永远无法参加会议的骑士踢出骑士团。
求最少要的踢出多少骑士。
解题思路
我们将没有憎恨关系的两个骑士连一条边,即作原图的补图。
根据题意,题目就是求出最长的奇环,用n减去长度就是答案。
这里我们先构造两个引理。
引理1:两个骑士属于不同的点双联通分量,那么它们将永远无法一起参加会议。
证明:(反证法)
设两个骑士为x和y,分别属于点双联通分量c[x]和c[y](c[x] != c[y])。
假设它们可以一起参加,即有一条同时包含x,y的奇环。
因为x和y在同一环中,所以无论删去哪个节点,都有x到y的路径。
而c[x]和c[y]中删掉一个点也是也是联通的,所以c[x]和c[y]组成的图也是点双联通分量。
所以假设不成立。
证毕。
引理2:一个点双联通分量中存在奇环,则该点双联通分量中任意两个点都被任意一个包含。
证明:
设x和y属于同一个点双联通分量且在同一个奇环中,z为该双联通分量中的任意一点。
那么x到y的路径加上x经过z到y的路径即为一个环。
因为一个奇数只能分成偶数+奇数,所以x到y的两条路径为一奇一偶。
无论x经过z到y的路径长为多少,所以一定有包含x,y和z的奇环。
证毕。
所以说,我们用tarjan跑出该图中的点双联通分量,如果这个点双联通中有奇环,那么这个点双联通分量中的骑士都不会被踢出,把他标记掉。
最后统计一下没有被标记的人数。
代码如下:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <stack> #define rep(x, l, r) for(int x = l; x <= r; x++) #define repd(x, r, l) for(int x = r; x >= l; x--) #define clr(x, y) memset(x, y, sizeof(x)) #define all(x) x.begin(), x.end() #define pb push_back #define mp make_pair #define MAXN 1005 #define MAXM 2000005 #define fi first #define se second #define SZ(x) ((int)x.size()) using namespace std; typedef long long LL; typedef vector<int> vi; typedef pair<int, int> pii; const int INF = 0x3f3f3f3f; const int p = 10000007; int lowbit(int x){ return x & -x; } int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p; } return x; } stack<int> sta; vector<int> dcc[MAXN]; int cnt, num, tot, now; int head[MAXN], nxt[MAXM], to[MAXM]; int dfn[MAXN], low[MAXN], c[MAXN], col[MAXN]; bool res, g[MAXN][MAXN], able[MAXN]; void init(){ cnt = num = tot = 0; clr(head, -1); clr(g, 0); clr(dfn, 0); clr(low, 0); clr(able, 0); clr(c, 0); } void add(int u, int v){ nxt[cnt] = head[u]; head[u] = cnt; to[cnt] = v; cnt++; } void tarjan(int u, int root){ dfn[u] = low[u] = ++num; sta.push(u); if(u == root && head[u] == 0){ dcc[++tot].pb(u); return; } for(int e = head[u]; e != -1; e = nxt[e]){ int v = to[e]; if(!dfn[v]){ tarjan(v, root); low[u] = min(low[u], low[v]); if(dfn[u] <= low[v]){ tot++; while(1){ int t = sta.top(); dcc[tot].pb(t); sta.pop(); if(t == v) break; } dcc[tot].pb(u); } } else low[u] = min(low[u], dfn[v]); } } void dfs(int u, int color){ col[u] = color; for(int e = head[u]; e != -1; e = nxt[e]){ int v = to[e]; if(c[v] != now) continue; if(!col[v]) dfs(v, 3 - color); else if(col[v] == color){ res = 1; return; } } } int main(){ int n, m; while(~scanf("%d%d", &n, &m) && n){ init(); rep(i, 1, n) dcc[i].clear(); while(!sta.empty()) sta.pop(); rep(i, 1, m){ int u, v; scanf("%d%d", &u, &v); g[u][v] = g[v][u] = 1; } rep(i, 1, n) rep(j, i + 1, n) if(!g[i][j]) add(i, j), add(j, i); rep(i, 1, n) if(!dfn[i]) tarjan(i, i); rep(i, 1, tot){ now = i; rep(j, 0, SZ(dcc[i]) - 1) c[dcc[i][j]] = now, col[dcc[i][j]] = 0;; res = 0; dfs(dcc[i][0], 1); if(res) rep(j, 0, SZ(dcc[i]) - 1) able[dcc[i][j]] = 1; } int ans = 0; rep(i, 1, n) if(!able[i]) ans++; printf("%d\n", ans); } return 0; }