KNIGHTS
题目描述见链接 .
正解部分
将原图的 补图 建出,
那么当一个节点被某个奇环覆盖时, 就存在一个方案, 使得这个节点参加会议 .
当得到所有被至少一个奇环覆盖的节点数量 cnt 后, 使用 总结点数量 减去 cnt 即可得到答案 .
那么现在的问题就是 cnt 如何求,
首先要知道 2 个引理 ↓
- 不同 <stron> 之间不会被同一个 奇环 覆盖 .</stron>
若存在一个奇环沟通两个 <stron>, 那么这两个 <stron> 可以合并 .</stron></stron>
- 在一个 <stron> 中, 若存在一个 奇环, 则所有点被至少一个 奇环 覆盖 .</stron>
设在 <stron> 中的 奇环 中存在两个点 u,v, 奇环 外部存在一个点 w, 由于 奇环 上 u,v 存在两条 奇偶性 不同的路径, u,v,w 可以构成 奇环 或 偶环 .</stron>
知道这 2 个引理之后, 就可以做这道题目了 .
使用 Tarjan 缩点, 判断每个 <stron> 中是否存在奇环即可, 二分图 中不存在 奇环, 二染色 来判断是否 二分图 即可 .</stron>
实现部分
#include<bits/stdc++.h>
#define reg register
#define pb push_back
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 1005;
int N;
int M;
int tim;
int Ans;
int num0;
int bk_cnt;
int low[maxn];
int dfn[maxn];
int head[maxn];
int bk_Mp[maxn];
int color[maxn];
bool Used[maxn];
bool In_stk[maxn];
bool is[maxn][maxn];
std::stack <int> stk;
std::stack <int> stk_2;
std::vector <int> bk[maxn];
struct Edge{ int nxt, to; } edge[maxn*maxn<<1];
void Add(int from, int to){ edge[++ num0] = (Edge){ head[from], to }; head[from] = num0; }
void Tarjan(int k, int fa){
low[k] = dfn[k] = ++ tim;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(!dfn[to]){
stk.push(k), stk_2.push(to);
Tarjan(to, k), low[k] = std::min(low[k], low[to]);
if(low[to] < dfn[k]) continue ;
bk_cnt ++;
while(!stk.empty()){
int tp = stk.top(), tu = stk_2.top();
bk[bk_cnt].pb(tp), bk[bk_cnt].pb(tu);
stk.pop(), stk_2.pop();
if(tp == k && tu == to) break ;
}
}else if(dfn[to] < dfn[k] && to != fa){
stk.push(k), stk_2.push(to);
low[k] = std::min(low[k], dfn[to]);
}
}
}
int col_cnt;
bool DFS(int k, int col, int id){
color[k] = col;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(id != bk_Mp[to]) continue ;
if(color[k] == color[to]) return false;
if(color[to] == -1){
color[to] = col ^ 1;
if(!DFS(to, col^1, id)) return false;
}
}
return true;
}
void Work(){
tim = num0 = 0;
for(reg int i = 1; i <= N; i ++) Used[i] = head[i] = dfn[i] = 0, color[i] = -1;
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= N; j ++) is[i][j] = 0;
for(reg int i = 1; i <= M; i ++){
int a = read(), b = read();
is[a][b] = is[b][a] = 1;
}
for(reg int i = 1; i <= N; i ++)
for(reg int j = i+1; j <= N; j ++)
if(!is[i][j]) Add(i, j), Add(j, i);
for(reg int i = 1; i <= N; i ++) if(!dfn[i]) Tarjan(i, 0);
for(reg int i = 1; i <= bk_cnt; i ++){
memset(color, -1, sizeof color);
for(reg int j = 0; j < bk[i].size(); j ++) bk_Mp[bk[i][j]] = i;
if(!DFS(bk[i][0], 0, i))
for(reg int j = 0; j < bk[i].size(); j ++) Used[bk[i][j]] = 1;
bk[i].clear();
}
Ans = N;
for(reg int i = 1; i <= N; i ++) Ans -= Used[i];
printf("%d\n", Ans);
}
int main(){
N = read(), M = read();
while(N && M){
Work();
N = read(), M = read();
}
return 0;
}