- 题目描述: 一群骑士,要有若干个(三个及以上的奇数个)骑士围成一个圈开会,有m组冲突关系,对于每一组(i,j),表示骑士i和骑士j开会不能相邻,求有多少个骑士一场会议都不能参加。
- 限制一:想象一下,一个圆桌是否可以看成一个简单环(一个双联通子图),假设现在有所有种的会议组合,对于一组冲突(i,j),相当于所有i,j相邻的圆桌会议组合都不合法了,即无向边(i,j)断开;因此第一个限制条件就有了处理办法:所有点构成一个团(点与点两两相连),将每组冲突对应的边从团中扣去。
- 现在考虑第二个限制条件:每个圆桌会议只能有奇数个骑士参加(奇环)。蓝书中有以下精彩证明:如果一个点·双联通分量是二分图,那么其一定不存在奇环,若不是二分图,则一定存在一个奇环。假设u1 u2为奇环中的两个点,则必然有u1->u2一奇一偶(针对点的数目说)的两条路径,也就是说不论路径u1->v->u2是奇是偶,总能构造出一个包含v的奇环。于是有:对于一个 点·双联通分量,若它不是二分图,则其中的每个点都在一个奇环中,都可以参加圆桌会议。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6f8049c455196a0de8baa2991b518a55.png)
- 如何判断是二分图:根据点·双连通分量的连通性,用染色法即可,如果到达的一个改分量中的点的颜色与当前点冲突,则不是二分图,具体详见代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include<bits/stdc++.h>
#include <vector>
#include <cstring>
#include <fstream>
using namespace std;
int n,m,last[1005],cnt,bccnt,low[1005],dfn[1005],dindex,stop,u,v,bccno[1005],color[1005],num;
bool g[1005][1005],iscut[1005],bccbl[1005][1005],ans[1005];
vector<int>bcc[1005];
struct edge{
int u,v,next;
}e[200010];
edge sta[200010];
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=last[u];
last[u]=cnt;
e[++cnt].v=u;
e[cnt].next=last[v];
last[v]=cnt;
}
void dfs(int u,int fa)
{
low[u]=dfn[u]=++dindex;
int child=0;
for(int i=last[u];i;i=e[i].next)
{
int v=e[i].v;
if(!dfn[v])
{
child++;
sta[++stop]={u,v,0};
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
iscut[u]=1;
bccnt++;
bcc[bccnt].clear();
while(1)
{
edge x=sta[stop--];
if(bccno[x.u]!=bccnt)
{
bccno[x.u]=bccnt;
bcc[bccnt].push_back(x.u);
bccbl[bccnt][x.u]=1;
}
if(bccno[x.v]!=bccnt)
{
bccno[x.v]=bccnt;
bcc[bccnt].push_back(x.v);
bccbl[bccnt][x.v]=1;
}
if(x.u==u&&x.v==v) break;
}
}
}
else if(v!=fa&&dfn[v]<dfn[u])
{
low[u]=min(low[u],dfn[v]);
sta[++stop]={u,v,0};
}
}
if(fa==-1&&child==1)
iscut[u]=0;
}
void findbcc()
{
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(bccno,0,sizeof(bccno));
memset(iscut,0,sizeof(iscut));
memset(bccbl,0,sizeof(bccbl));
dindex=bccnt=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i,-1);
}
bool getcolor(int x,int bc)
{
for(int i=last[x];i;i=e[i].next)
{
int v=e[i].v;
if(!color[v]&&bccbl[bc][v])
{
color[v]=(color[x]==1?2:1);
if(!getcolor(v,bc)) return false;
}
else if(color[v]==color[x]&&bccbl[bc][v])
return false;
}
return true;
}
void intt()
{
for(int i=1;i<=cnt;i++)
e[i].v=e[i].next=0;
memset(last,0,sizeof(last));
memset(g,0,sizeof(g));
memset(ans,0,sizeof(ans));
num=cnt=0;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n+m!=0)
{
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
g[v][u]=g[u][v]=1;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(!g[i][j])
add(i,j);
findbcc();
for(int i=1;i<=bccnt;i++)
{
for(int j=0;j<bcc[i].size();j++)
color[bcc[i][j]]=0;
color[bcc[i][0]]=1;
if(!getcolor(bcc[i][0],i))
for(int j=0;j<bcc[i].size();j++)
ans[bcc[i][j]]=1;
}
for(int i=1;i<=n;i++)
if(!ans[i]) num++;
cout<<num<<endl;
intt();
}
return 0;
}