Knights of the Round Table
题目背景:
poj2942
分析:这个题,每个知识点都不难,但是放到一起还是挺恶心的。首先我们明确我们要找的是点双联通分量,然后针对每一组点双联通分量进行二分图染色,一次来判定其是否为奇圈(满足题目要求),之后用总数减去奇圈中的点个数即可。
Source:
#include #include #include #include #include #include #include #include using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p = false; c = getchar(); } while(isdigit(c)) { v = (v << 3) + (v << 1) + (c ^ '0'); c = getchar(); } if(!p) v = -v; } const int MAXN = 600 + 10; const int MAXM = 1000000 + 100; stack s; int first[MAXN << 1], low[MAXN << 1], num[MAXN << 1], scc[MAXN << 1], scnt[MAXN << 1]; int n, m, x, y, z, cnt, tot, ind, color[MAXN << 1], in[MAXN << 1], temp[MAXN << 1]; bool exist[MAXN << 1], map[2000][2000], odd[MAXN << 1]; struct node { int next, to; } edge[MAXM << 1]; inline void create(int x, int y) { tot++; edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; } void pre() { /*多组数据,所以注意初始化*/ tot = ind = 0; memset(num, 0, sizeof(num)); memset(map, 0, sizeof(map)); memset(exist, 0, sizeof(exist)); memset(odd, 0 ,sizeof(odd)); memset(first, 0, sizeof(first)); } void read() { for(int i = 1; i <= m; ++i) R(x), R(y), map[x][y] = map[y][x] = true; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) if(i != j && !map[i][j]) create(i, j); } bool dfs1(int cur, int c) { /*双联通判奇环*/ /*直接用简单的二分图染色即可*/ color[cur] = c; for(int p = first[cur]; p; p = edge[p].next) { if(!in[edge[p].to]) continue; if(color[edge[p].to] == c) return true; else if(color[edge[p].to]) continue; else if(dfs1(edge[p].to, 3 - c)) return true; } return false; } void dfs(int cur, int fa) { num[cur] = low[cur] = ++ind; exist[cur] = true; s.push(cur); for(int p = first[cur]; p; p = edge[p].next) { if(!num[edge[p].to]) { int top = 0; dfs(edge[p].to, cur), low[cur] = min(low[edge[p].to], low[cur]); if(low[edge[p].to] >= num[cur]) { /*点双联的另一种求法,找到一个子节点满足上述*/ memset(in, false, sizeof(in)); int o = s.top(); while(o != edge[p].to) { exist[o] = false, s.pop(), in[o] = true; temp[++top] = o, o = s.top(); } /*当前栈中的在自己之上的所有点,包括自己都是一个双联通* /*出栈的时候自己不出栈,因为割点可能存在于多个双联通*/ exist[o] = false, s.pop(), in[o] = true; temp[++top] = o, in[cur] = true; memset(color, 0, sizeof(color)); if(dfs1(cur, 1)) { odd[cur] = true; while(top) odd[temp[top--]] = true; } } } else if(edge[p].to != fa && exist[edge[p].to]) low[cur] = min(low[cur], num[edge[p].to]); } } void work() { int ans = 0; for(int i = 1; i <= n; ++i) if(!num[i]) dfs(i, -1); for(int i = 1; i <= n; ++i) if(!odd[i]) ans++; cout << ans << '\n'; } int main() { while(scanf("%d%d", &n, &m) != EOF) { if(n == 0 && m == 0) break; pre(); read(); work(); } return 0; }