POJ传送门
Luogu传送门
UVA传送门
题意翻译
亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:
1、 相互憎恨的两个骑士不能坐在直接相邻的2个位置;
2、 出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。
如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。
现在给定准备去开会的骑士数 n n n,再给出 m m m对憎恨对(表示某 2 2 2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议?
注意:
1.所给出的憎恨关系一定是双向的,不存在单向憎恨关系。
2.由于是圆桌会议,则每个出席的骑士身边必定刚好有 2 2 2个骑士。即每个骑士的座位两边都必定各有一个骑士。
3.一个骑士无法开会,就是说至少有 3 3 3个骑士才可能开会。
输入输出格式
输入格式
题目包含多组数据。
每一组数据的第一行为两个整数 n , m n,m n,m。 如果 n = m = 0 n=m=0 n=m=0则代表输入结束, 你不必处理这组数据。
以下 m m m行, 每行两个正整数, 表示一组憎恨关系。
输出格式
对于每组数据, 输出一行一个正整数, 表示最少需要剔除掉的骑士个数。
输入输出样例
输入样例
5 5
1 4
1 5
2 5
3 4
4 5
0 0
输出样例
2
解题分析
来补点双的锅了QAQ
首先, 我们把这个图转成其补图, 那么问题就变成了有多少个点不在任何一个奇环中。
注意到任何一个点双中, 如果有一个奇环, 那么所有点都可以至少在一个奇环中(因为奇环会被拆成两部分, 一截长度为偶数, 一截为奇数, 而点双中无割点, 我们可以选一条凑出一个奇环)。
所以我们直接缩点双, 然后大力染色判奇环即可。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cstring>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1050
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
int n, m, cnt, arr, top, tot;
struct Edge {int to, nex;} edge[MX * MX * 2];
int head[MX], col[MX], low[MX], dfn[MX], buc[MX], sta[MX];
bool mp[MX][MX], ok[MX], init[MX];
IN void add(R int from, R int to)
{edge[++cnt] = (Edge){to, head[from]}, head[from] = cnt;}
bool dye(R int now, R int fa, R int cl)
{
col[now] = cl;
for (R int i = head[now]; i; i = edge[i].nex)
{
if (!init[edge[i].to]) continue;
if (~col[edge[i].to])
{
if (col[edge[i].to] == col[now]) return false;
else continue;
}
else if (!dye(edge[i].to, now, cl ^ 1)) return false;
}
return true;
}
void DFS(R int now, R int fa)
{
dfn[now] = low[now] = ++arr;
sta[++top] = now;
for (R int i = head[now]; i; i = edge[i].nex)
{
if (edge[i].to == fa) continue;
if (!dfn[edge[i].to])
{
DFS(edge[i].to, now);
low[now] = min(low[now], low[edge[i].to]);
if (low[edge[i].to] >= dfn[now])
{
tot = 0;
do {buc[++tot] = sta[top--];}
W (buc[tot] ^ edge[i].to);
if (tot == 1) continue;
buc[++tot] = now;
for (R int j = 1; j <= tot; ++j) init[buc[j]] = true, col[buc[j]] = -1;
if (!dye(now, 0, 0))
for (R int j = 1; j <= tot; ++j) ok[buc[j]] = true;
for (R int j = 1; j <= tot; ++j) init[buc[j]] = false;
}
}
else low[now] = min(low[now], dfn[edge[i].to]);
}
}
int main(void)
{
int foo, bar, ans;
W (233)
{
std::memset(head, arr = cnt = 0, sizeof(head));
std::memset(mp, false, sizeof(mp));
std::memset(ok, false, sizeof(ok));
std::memset(dfn, ans = 0, sizeof(dfn));
in(n), in(m);
if (n == 0 && m == 0) break;
for (R int i = 1; i <= m; ++i)
{
in(foo), in(bar);
mp[foo][bar] = true;
mp[bar][foo] = true;
}
for (R int i = 1; i < n; ++i)
for (R int j = i + 1; j <= n; ++j)
if (!mp[i][j]) add(i, j), add(j, i);
for (R int i = 1; i <= n; ++i) if (!dfn[i]) DFS(i, 0);
for (R int i = 1; i <= n; ++i) if (ok[i]) ++ans;
printf("%d\n", n - ans);
}
}