点双连通分量即子图中没有割点
const int maxn=1e3+5;
struct edge
{
int u,v;
};
int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
vector <int> g[maxn],bcc[maxn];//bcc存储每个双连通分量的点
stack<edge> s;
int dfs(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
int child=0;
for(int i=0; i<g[u].size(); i++)
{
int v=g[u][i];
edge e=(edge)
{
u,v
};
if(!pre[v])
{
s.push(e);
child++;
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])
{
iscut[u]=true;
bcc_cnt++;
bcc[bcc_cnt].clear();
while(1)
{
edge x=s.top();
s.pop();
if(bccno[x.u]!=bcc_cnt)//x.u没有添加过
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u]=bcc_cnt;
}
if(bccno[x.v]!=bcc_cnt)//x.v没有添加过
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v]=bcc_cnt;
}
if(x.u==u&&x.v==v)//抵达起点
break;
}
}
}
else if(pre[v]<pre[u]&&v!=fa)
{
s.push(e);
lowu=min(lowu,pre[v]);
}
}
if(fa<0&&child==1)
iscut[u]=0;
return lowu;
}
void find_bcc(int n)
{
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(bccno,0,sizeof(bccno));
dfs_clock=bcc_cnt=0;
for(int i=1; i<=n; i++)//注意顶点序号是从0还是从1开始
{
if(!pre[i])
dfs(i,-1);
}
}
题目链接:https://vjudge.net/problem/POJ-2942
题意:有n个人,m条边表示憎恨关系,其中关系是双向的,现在有一个会议,所有人围成一个圈。其中有憎恨关系的不能相邻,并且最终参加会议人数为奇数,求有多少人不能参加任何一场会议。
思路:参考https://blog.csdn.net/lyy289065406/article/details/6756821。两个定理:1.简单圈上的节点属于同一个连通分量 2.如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。 先把图中的双连通分量求出来,然后挨个判断每个双连通分量是否是一个二分图即可。能够参加会议的人数就是所有不是二分图的双连通分量的人数和。注意可能会有重复的人。
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <stack>
using namespace std;
const int maxn=1e3+5;
struct edge
{
int u,v;
};
int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
vector <int> g[maxn],bcc[maxn];
stack<edge> s;
int dfs(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
int child=0;
for(int i=0; i<g[u].size(); i++)
{
int v=g[u][i];
edge e=(edge)
{
u,v
};
if(!pre[v])
{
s.push(e);
child++;
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])
{
iscut[u]=true;
bcc_cnt++;
bcc[bcc_cnt].clear();
while(1)
{
edge x=s.top();
s.pop();
if(bccno[x.u]!=bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u]=bcc_cnt;
}
if(bccno[x.v]!=bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v]=bcc_cnt;
}
if(x.u==u&&x.v==v)
break;
}
}
}
else if(pre[v]<pre[u]&&v!=fa)
{
s.push(e);
lowu=min(lowu,pre[v]);
}
}
if(fa<0&&child==1)
iscut[u]=0;
return lowu;
}
void find_bcc(int n)
{
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(bccno,0,sizeof(bccno));
dfs_clock=bcc_cnt=0;
for(int i=0; i<n; i++)
{
if(!pre[i])
dfs(i,-1);
}
}
int a[maxn][maxn],odd[maxn],color[maxn];
bool judge(int u,int b)//判断二分图
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(bccno[v]!=b)
continue;
if(color[v]==color[u])
return false;
if(!color[v])
{
color[v]=3-color[u];
if(!judge(v,b))
return false;
}
}
return true;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m))
{
if(n==0&&n==m)
break;
for(int i=0; i<n; i++)
{
g[i].clear();
}
memset(a,0,sizeof(a));
for(int i=0; i<m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
u--,v--;
a[u][v]=a[v][u]=1;
}
for(int i=0; i<n; i++)
{
for(int j=i+1; j<n; j++)
{
if(!a[i][j])
{
g[i].push_back(j);
g[j].push_back(i);
}
}
}
find_bcc(n);
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];
color[u]=1;
if(!judge(u,i))
{
for(int j=0; j<bcc[i].size(); j++)
{
odd[bcc[i][j]]=1;
}
}
}
int ans=n;
for(int i=0; i<n; i++)
if(odd[i])
ans--;
printf("%d\n",ans);
}
return 0;
}