题解:
- 首先把不敌对的骑士连一条边,即原图的补图。
- 要开一个会议,得形成一个环。所以求点-双连通分量。
- 考虑每个连通分量,人数必须为奇数个。所以进行二分图奇偶染色。
- 这里有一个结论:二分图一定是偶圈。如果染色失败,那么说明是奇圈,满足条件。
- 还有一个结论:如果存在一个环为奇圈,那么整个连通分量的每一点都在某个奇圈当中。
代码:
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int const N = 1000 + 10;
int n,m,mp[N][N];
int cnt,bcc_cnt;
int dfn[N],lowlink[N],bccno[N];
int color[N],odd[N];
vector<int>G[N],bcc[N];
struct Edge
{
int u,v;
Edge(int uu,int vv):u(uu),v(vv){};
};
stack<Edge>st;
void Init(){
for(int i=1;i<=n;i++) G[i].clear();
memset(mp,0,sizeof(mp));
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
mp[u][v] = mp[v][u] = true;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(!mp[i][j]) G[i].push_back(j), G[j].push_back(i);
}
void dfs(int u,int fa){
dfn[u] = lowlink[u] = ++cnt;
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(v == fa) continue;
if(!dfn[v]){
st.push(Edge(u,v));
dfs(v,u);
lowlink[u] = min(lowlink[v],lowlink[u]);
if(lowlink[v] >= dfn[u]){
bcc_cnt++; bcc[bcc_cnt].clear();
while(1){
Edge e = st.top(); st.pop();
if(bccno[e.u] != bcc_cnt) bcc[bcc_cnt].push_back(e.u), bccno[e.u] = bcc_cnt;
if(bccno[e.v] != bcc_cnt) bcc[bcc_cnt].push_back(e.v), bccno[e.v] = bcc_cnt;
if(e.u == u && e.v == v) break;
}
}
}else if(dfn[v] < dfn[u]){
st.push(Edge(u,v));
lowlink[u] = min(lowlink[u],dfn[v]);
}
}
}
void Tarjan(){
cnt = bcc_cnt = 0;
memset(dfn,0,sizeof(dfn));
memset(lowlink,0,sizeof(lowlink));
memset(bccno,0,sizeof(bccno));
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i,0);
}
bool dfs2(int u,int c,int bcc){
color[u] = c;
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(bccno[v] != bcc) continue;
if(color[v] == c) return false;
if(!color[v] && !dfs2(v,-c,bcc)) return false;
}
return true;
}
int solve(){
memset(odd,0,sizeof(odd));
for(int i=1;i<=bcc_cnt;i++){
memset(color,0,sizeof(color));
for(int j=0;j<bcc[i].size();j++) bccno[bcc[i][j]] = i; //这里必须更新,因为一个定点可能属于多个连通分量
int u = bcc[i][0];
if(!dfs2(u,1,i)){
for(int j=0;j<bcc[i].size();j++) odd[bcc[i][j]] = true;
}
}
int sum = n;
for(int i=1;i<=n;i++) if(odd[i]) sum--;
return sum;
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(!n && !m) break;
Init();
Tarjan();
printf("%d\n",solve());
}
return 0;
}