///自己试着敲试试
//莫名奇妙的过了,我还是要好好研究原理。
//点双联通
//每个点与其他点都至少有两条路径。
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define maxn 1020
#define maxm 1000010
using namespace std;
bool is_in[maxn];
int dfn[maxn];
int low[maxn];
int g[maxn][maxn];
int cnt,top;
int n,m,ans;
int head[maxn];
int color[maxn];
bool counted[maxn],vis[maxn];
bool is_cut_vertex[maxn];
struct Edge
{
int v,next;
}edge[2*maxm];
struct Elem
{
int u,v;
Elem(){}
Elem(int uu,int vv):u(uu),v(vv){}
}stk[maxm*2];
void add(Elem &temp)
{
is_in[temp.u]=true;
is_in[temp.v]=true;
}
void init()
{
memset(is_in,false,sizeof(is_in));
}
void addedge(int a,int b)
{
edge[cnt].next=head[a];
edge[cnt].v=b;
head[a]=cnt++;
}
int counter()
{
int ans=0;
for(int i=1;i<=n;i++)
if(is_in[i]&&!counted[i])
{
ans++;counted[i]=true;
}
return ans;
}
对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时
每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从
栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,
组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支。
int judgeodd(int temp)///我觉得这里判断奇环的方式还是不大好理解
///这里is_in为真的点就是当前在点双联通分支里的点。。。还真是点双联通分支的点
{
memset(color,-1,sizeof(color));
queue<int>q;
q.push(temp);
color[temp]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].v;
if(!is_in[v]) continue;
if(~color[v]&& color[v]==color[u])
return counter();
if(color[v]==-1)
{
q.push(v);
color[v]=color[u]^1;
}
}
}
return 0;
}
void tarjan(int dep, int parent, int u)///初始dfs树深度为0,父为-1,该节点为i
{
int descendant = 0;
dfn[u] = low[u] = dep;///初始化
vis[u] = true;
stk[top++] = Elem(parent, u);
//cout<<"maya"<<endl;
for (int i = head[u]; ~i; i = edge[i].next)///遍历所有邻接边
{
int v = edge[i].v;
if (v == parent)
continue;
if (vis[v])
{
low[u] = min(low[u], dfn[v]);///按邻接点更新low值
continue;
}
tarjan(dep + 1, u, v);
low[u] = min(low[u], low[v]);
if (low[v] >= dep)
descendant++;
if (low[v] == dep)///找到头了。
{
init();
while (!(stk[top].u == u && stk[top].v == v))///一直加到当前存储的边所在的边
add(stk[--top]);///放入连通块 中
ans += judgeodd(v);///返回可以的个数
}
}
if (low[u] == dep)
top--;
if (parent == -1)
descendant--;
if (descendant > 0)
is_cut_vertex[u] = true;///居然是用来判断割点的,好像和本题无关。。
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0 && m==0) break;
top=0;
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
memset(counted,false,sizeof(counted));
memset(g,false,sizeof(g));
cnt=0;
int tempa,tempb;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&tempa,&tempb);
g[tempa][tempb]=g[tempb][tempa]=true;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!g[i][j] && i!=j)
addedge(i,j);
ans=0;
for(int i=1;i<=n;i++)
if(!vis[i])
{
// cout<<i<<endl;
tarjan(0,-1,i);
}
ans=n-ans;
cout<<ans<<endl;
}
}
POJ 2942 点双连通
最新推荐文章于 2020-12-25 22:45:08 发布