标签:
做这题简直是一种折磨。。。
有n个骑士,骑士之间相互憎恨。给出骑士的相互憎恨的关系。 骑士要去开会,围成一圈坐,相互憎恨的骑士不能相邻。开会骑士的个数不能小于三个人。求有多少个骑士不能开会。
注意:会议可以开无数次,也就是说一个骑士其实是可以开多次会议的,所以一共可以开会的人也未必是奇数。
求出相互并不憎恨的骑士的关系图,也就是相连的骑士可以挨着。这样如果有一个奇数圈就可以确定一圈的人全部可以参加会议。
性质:如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中
又知道二分图染色可以判断奇数圈,所以对每个点的强连通分量求二分图染色就ok了。这题的关键在于求点的双连通分量。
/********************************************* Memory: 4752 KB Time: 1141 MS Language: G++ Result: Accepted *********************************************/ #include <iostream> #include <cstdio> #include <cstring> #define pk puts("kkk"); using namespace std; const int N = 1005; const int M = N * N; struct Edge { int to, next; } edge[M]; int head[N]; int cnt_edge; void add_edge(int u, int v) { edge[cnt_edge].to = v; edge[cnt_edge].next = head[u]; head[u] = cnt_edge++; edge[cnt_edge].to = u; edge[cnt_edge].next = head[v]; head[v] = cnt_edge++; } int dfn[N], low[N], idx; int stk[N], top; int kind[N], cnt; bool ok[N], in[N]; int color[N]; int mp[N][N]; int n; bool dfs_color(int u, int c) { color[u] = c; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (in[v]) { if (!color[v] && !dfs_color(v, 3 - c)) return false; else if (color[v] == color[u]) return false; } } return true; } void dfs(int u, int pre) { dfn[u] = low[u] = ++idx; stk[++top] = u; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == pre) continue; if (!dfn[v]) { dfs(v, u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u]) // u是割点, 求双连通分量 { memset(in, 0, sizeof in); memset(color, 0, sizeof color); cnt = 0; int x; int num = 0; do { x = stk[top--]; kind[cnt++] = x; in[x] = true; num++; } while (x != v); if (num <= 1) continue; in[u] = true; if (!dfs_color(u, 1)) { ok[u] = true; while (cnt--) { ok[ kind[cnt] ] = true;} } } } else low[u] = min(low[u], dfn[v]); } } void solve() { int ans = 0; for (int i = 1; i <= n; ++i) dfs(i, -1); for (int i = 1; i <= n; ++i) if (ok[i]) ans++; printf("%d\n", n - ans); } void init() { memset(head, -1, sizeof head); memset(dfn, 0, sizeof dfn); memset(ok, 0, sizeof ok); memset(mp, 0, sizeof mp); cnt_edge = top = idx = 0; } int main() { int m; int u, v; while (~scanf("%d%d", &n, &m)) { if (n == 0 && m == 0) break; init(); for (int i = 1; i <= m; ++i) { scanf("%d%d", &u, &v); mp[u][v] = mp[v][u] = 1; } for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if (i != j && !mp[i][j]) add_edge(i, j); solve(); } return 0; }
poj 2942--Knights of the Round Table (点的双连通分量)
标签:
原文地址:http://www.cnblogs.com/wenruo/p/5008032.html