这题的题解网上有一大堆。可以自己去搜。
但题解中都用了一个的结论: 如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中;
想了好久都没有想出证明来。后来问了天王总于搞懂为什么了。
贴出原话: 设奇圈内的点为A,外的点为B,它们处在同一双强连通分量,也就是说至少有两条不相交路径互达,设这两条路径与奇圈的交点为C和D(注意C和D肯定不都是A点,否则A点成了关节点,所以C和D不相同),于是新生成了两个圈BCD和BDC,其中必有一个奇圈。
自己照着画个图,想想各种情况就懂了。
代码:
/*
* Author: xioumu
* Created Time: 2012-2-5 14:36:51
* File Name: c.cpp
* solve: c.cpp
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define MAXN 1007
using namespace std;
int a[MAXN][MAXN],f[MAXN];
int n,m,ans;
void init(){
int i,j,k,r,w;
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
a[i][j] = a[j][i] = 1;
for(i=1;i<=m;i++){
scanf("%d %d",&r,&w);
a[r][w] = a[w][r] = 0;
}
}
int zhan[MAXN],top,v[MAXN],df[MAXN],low[MAXN],num;
int d[MAXN];
bool pan(int w)
{
int i,j,k;
for(i=1;i<=n;i++){
if(a[w][i] && v[i] != 0){
if(v[i] == 1){
v[i] = (v[w]-1)%2 + 2;
if( !pan(i) ) return false;
}
else if( (v[w]-1)%2 + 2 != v[i])
return false;
}
}
return true;
}
void dfs(int w,int fa){
int i,j,k,r;
df[w] = low[w] = ++num;
zhan[++top] = w;
for(i=1;i<=n;i++)
if(a[w][i] && i != fa){
if(df[i] == 0){
dfs(i,w);
low[w] = min(low[w],low[i]);
if(low[i] >= df[w]){
memset(v,0,sizeof(v));
k = top;
do{
v[ zhan[top] ] = 1;
//printf("%d ",zhan[top]);
top--;
}while(zhan[top+1] != i);
v[w]=1;
//printf("%d\n",w);
if(!pan(w) ){
//printf("===%d %d\n",v[1],v[2]);
for(k=1;k<=n;k++)
if(v[k] >= 1) {
d[k] = 1;
//printf("%d\n",v[5]);
}
}
}
}
else low[w] = min(low[w],df[i]);
}
}
void solve(){
int i,j,k,r,w;
ans = 0;
memset(f,0,sizeof(f));
top = num = 0;
memset(df,0,sizeof(df));
memset(low,0,sizeof(low));
memset(v,0,sizeof(v));
memset(d,0,sizeof(d));
for(i=1;i<=n;i++){
if(df[i] == 0){
dfs(i,0);
}
}
for(i=1;i<=n;i++)
if(d[i] == 0){
//printf("%d\n",i);
ans++;
}
printf("%d\n",ans);
}
int main()
{
while(scanf("%d %d",&n,&m) != EOF && n && m){
init();
solve();
}
return 0;
}