联通分量是我以前研究了很久的东西,但是发现还是有很多理解不透彻。
知识还是要反复咀嚼。
这题求的是每个点是否存在于一个奇环中。
首先求点联通分量。
所谓点双连通分量,就是说有两条没有重复点的路径相连的两个点就处在同一个点双连通分量中。
这个算法也可以求出割点(当删掉这个点,将本来联通的一块将变得不连通)的个数。
然后,用交叉染色法判断这个联通分量是否存在奇环。
可以证明的是联通分量中存在奇环,则这个联通分量中所有点都至少在一个奇环中。
用交叉染色法,判断是否存在奇环。就是颜色交替地dfs或bfs。这个证明可以参考
http://my.oschina.net/mustang/blog/85987
这位大牛的文章。
注意本题只有一个点的环是不合法的。不过在交叉染色法中也会过滤掉这种情况。但是如果题目没有这个条件,就需要特判加上这种奇环了。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define NN 1010
#define MM 2010000
int first[NN],next[MM],v[MM],dfn[NN],low[NN];
int tote,n,m,a,b,mx,totdfn,totb,top,sta[NN],tot;
int block[NN][NN];
int sat[NN],cnt[NN];
int g[NN][NN];
int col[NN];
inline void addedge(int from,int to){
tote++;
next[tote]=first[from];
first[from]=tote;
v[tote]=to;
}
int dfs(int now,int ncol){
int e,vv;
col[now]=ncol;
for(e=first[now];e!=-1;e=next[e]){
vv=v[e];
if (col[vv]==ncol){
return 1;
}
else if (col[vv]==2){
if (dfs(vv,ncol^1)) return 1;
}
}
return 0;
}
int check(int bnum){
int i;
if (cnt[bnum]==1) return 0;
memset(col,-1,sizeof(col));
for(i=1;i<=cnt[bnum];++i){
col[block[bnum][i]]=2;
}
return dfs(block[bnum][1],0);
}
void tarjan(int u,int fa){
//printf("%d %d\n",u,fa);
int tmp,vv,e;
dfn[u]=low[u]=++totdfn;
sta[++top]=u;
for(e=first[u];e!=-1;e=next[e]){
vv=v[e];
if (!dfn[vv]){
tarjan(vv,u);
if (low[vv]<low[u]) low[u]=low[vv];
if (low[vv]>=dfn[u]) { //这句是与边双连通分量算法的主要差别
totb++;
do{
tmp=sta[top--];
block[totb][++cnt[totb]]=tmp;
}while(tmp!=vv);
block[totb][++cnt[totb]]=u;
if (check(totb)){
for(int i=1;i<=cnt[totb];++i){
sat[block[totb][i]]=1;
}
}
}
}
else if (fa!=vv&&dfn[vv]<low[u]) low[u]=dfn[vv];
}
}
int bcc(){
memset(dfn,0,sizeof(dfn));
memset(sat,0,sizeof(sat));
memset(cnt,0,sizeof(cnt));
totb=top=totdfn=0;
int i;
for(i=1;i<=n;i++)if (!dfn[i]){
tarjan(i,-1);
}
int ans=0;
for(i=1;i<=n;++i){
if (sat[i]==0) ans++;
}
return ans;
}
int main(){
int i,j,ans;
while(scanf("%d%d",&n,&m)&&(n||m)){
memset(first,-1,sizeof(first));
tote=0;
memset(g,0,sizeof(g));
for(i=1;i<=m;i++){
scanf("%d%d",&a,&b);
g[a][b]=g[b][a]=1;
}
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
if (!g[i][j]&&i!=j) addedge(i,j);
ans=bcc();
printf("%d\n",ans);
}
return 0;
}