POJ 2942 点双连通

///自己试着敲试试
//莫名奇妙的过了,我还是要好好研究原理。
//点双联通
//每个点与其他点都至少有两条路径。

#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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值