題目鏈接:傳送門
有仇恨的騎士不能相鄰,那就在沒有仇恨的騎士之間連邊,意義就是他們可以相鄰
要求騎士的座位形成一個環,那麼入度爲0或者爲1的騎士是無法被加進來的
而且要求環是有奇數個點,這就用到判斷奇環的方法,其實就是判斷這個圖是不是二分圖,交叉染色判斷即可
找到每個符合條件的環之後將環內的騎士標號,最後答案減去這些騎士就是要被踢出去的
這裏的環要用判斷割點的方法而不能縮點,因爲我們只需要找到一個個的連通分量,從小的環開始判斷才能獲得更多的環使答案更優
#include <bits/stdc++.h>
#define A 1010
#define B 2000010
using namespace std;
typedef long long ll;
struct node {int next, to;}e[B];
int head[A], num;
void add(int fr, int to) {e[++num].next = head[fr]; e[num].to = to; head[fr] = num;}
int n, m, a, b; bool hate[A][A], exi[A], bl[A];
int dfn[A], cnt, low[A], col[A], sta[A], top;
vector<int> v;
bool check(int s) {
queue<int> q; q.push(s); col[s] = 1;
while (!q.empty()) {
int fr = q.front(); q.pop(); int color = (col[fr] == 1 ? 2 : 1);
for (int i = head[fr]; i; i = e[i].next) {
int ca = e[i].to;
if (!exi[ca]) continue;
if (!col[ca]) col[ca] = color, q.push(ca);
else if (col[ca] != color) return true;
}
}
return false;
}
void tarjan(int fr) {
dfn[fr] = low[fr] = ++cnt; sta[++top] = fr;
for (int i = head[fr]; i; i = e[i].next) {
int ca = e[i].to;
if (!dfn[ca]) {
tarjan(ca); low[fr] = min(low[fr], low[ca]);
if (dfn[fr] <= low[ca]) {
v.clear(); int p;
do {
p = sta[top--];
v.push_back(p);
} while (p != ca);
v.push_back(fr);
memset(col, 0, sizeof col); memset(exi, 0, sizeof exi);
for (auto j : v) exi[j] = 1;
if (check(v[0])) for (auto j : v) bl[j] = 1;
}
}
else low[fr] = min(low[fr], dfn[ca]);
}
}
int main(int argc, char const *argv[]) {
while (scanf("%d%d", &n, &m) and n and m) {
memset(head, 0, sizeof head); num = 0; cnt = 0; memset(bl, 0, sizeof bl);
memset(dfn, 0, sizeof dfn); memset(low, 0, sizeof low); top = 0;
memset(sta, 0, sizeof sta); memset(hate, 0, sizeof hate);
for (int i = 1; i <= m; i++) scanf("%d%d", &a, &b), hate[a][b] = hate[b][a] = 1;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
if (!hate[i][j]) add(i, j), add(j, i);
for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);
int ans = n;
for (int i = 1; i <= n; i++) if (bl[i]) ans--;
printf("%d\n", ans);
}
return 0;
}