边连通分量/缩点-POJ3177-Redundant Paths

来源:互联网 发布:excel一行数据分成多行 编辑:程序博客网 时间:2024/06/05 10:46

用tarjan 的强连通分量增加了两句话得到的求边连通分量的方法

也就是因为无向图加了两条边之后有连回去的边,所以要判断下一个子节点是不是父亲节点,其余的和有向图的强连通分量没差。

这个算法就是记录两个时间戳,一个是index,一个是low。不断dfs。然后通过子节点更新low 这样把子节点更新完之后,属于同一个边连通分量(可以两条路径经过同一个点,但是不能经过通一条边的叫做边连通分量)的点都入栈了。然后当前时间戳即为最小时间戳,此时退栈并记录节点。

<pre name="code" class="cpp">stack<int>sta;void init2(){    e = 0;    memset(head,-1,sizeof(head));}struct p1{    int index,low;}nar[SIZE_D];int Dindex,scc;int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];void inittarjan(){    Dindex = 0;    memset(insta,0,sizeof(insta));    scc = 0;    memset(num,0,sizeof(num));}void tarjan(int ver){    used[ver] = 1;    nar[ver].index = Dindex;//记录当前的时间戳    nar[ver].low = Dindex;//记录最早的时间戳    Dindex++;//时间戳++;    sta.push(ver);//当前节点入栈    insta[ver] = 1;    for (int i = head[ver]; i != -1; i = pra[i].next){        int u = pra[i].to;        if (used[u] == -1){//遍历子节点。如果子节点没有被访问过,则dfspar[u] = ver;            tarjan(u);            nar[ver].low = min(nar[ver].low, nar[u].low);//最早的时间戳更新        }else{            if (insta[u] == 1)//如果已经在栈内。更新当前的最早时间戳if (u != par[ver])                nar[ver].low = min(nar[ver].low, nar[u].low);        }    }    if (nar[ver].low == nar[ver].index){//dfs结束后回到了最早的时间戳的那个顶点        scc++;        int w;        do{            w = sta.top();//出栈            sta.pop();            insta[w] = 0;            belong[w] = scc;//记录这个节点属于哪个连通分量            num[scc]++;//这个连通分量里面的节点个数加一        }while (w != ver);//直到取到当前节点为止。    }}

题意:让你最少加多少条边可以没有桥(就是去掉这条边,就不连通了。这条边就是桥),可能会有重边

题解:缩点——用tarjan 的边连通分量可以缩点。

然后结果就是(出度为1的点的个数+1)/ 2

判断每个节点是否和子节点属于同一个连通分量,如果不属于。那么这两个连通分量自加1,注意是强连通分量+1不是顶点+1

因为是无向图 所以出度为2就是有向图里的出度为1

这个重边好坑啊!

思考重边会不会对结果造成影响,然后读入的时候要判断一下有没有重边。引申,读入还要判断一下有没有自环!!!(自己连到自己的节点上)

思考重边会不会对结果造成影响,然后读入的时候要判断一下有没有重边。引申,读入还要判断一下有没有自环!!!

思考重边会不会对结果造成影响,然后读入的时候要判断一下有没有重边。引申,读入还要判断一下有没有自环!!!

思考重边会不会对结果造成影响,然后读入的时候要判断一下有没有重边。引申,读入还要判断一下有没有自环!!!

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<stack>#define SIZE_D 5005#define SIZE_B 50005using namespace std;int e,head[SIZE_D];struct pp{    int to,next;}pra[SIZE_B];void addedge(int x,int y){    pra[e].to = y;    pra[e].next = head[x];    head[x] = e++;    pra[e].to = x;    pra[e].next = head[y];    head[y] = e++;}int repeat(int x, int y){    for (int i = head[x]; i != -1; i = pra[i].next){        if (pra[i].to == y)            return 0;    }    return 1;}int out[SIZE_D];int D,B;stack<int>sta;void init2(){    e = 0;    memset(head,-1,sizeof(head));}struct p1{    int index,low;}nar[SIZE_D];int Dindex,scc;int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];void inittarjan(){    Dindex = 0;    memset(insta,0,sizeof(insta));    scc = 0;    memset(num,0,sizeof(num));}void tarjan(int ver){    used[ver] = 1;    nar[ver].index = Dindex;//记录当前的时间戳    nar[ver].low = Dindex;//记录最早的时间戳    Dindex++;//时间戳++;    sta.push(ver);//当前节点入栈    insta[ver] = 1;    for (int i = head[ver]; i != -1; i = pra[i].next){        int u = pra[i].to;        if (used[u] == -1){//遍历子节点。如果子节点没有被访问过,则dfspar[u] = ver;            tarjan(u);            nar[ver].low = min(nar[ver].low, nar[u].low);//最早的时间戳更新        }else{            if (insta[u] == 1)//如果已经在栈内。更新当前的最早时间戳if (u != par[ver])                nar[ver].low = min(nar[ver].low, nar[u].low);        }    }    if (nar[ver].low == nar[ver].index){//dfs结束后回到了最早的时间戳的那个顶点        scc++;        int w;        do{            w = sta.top();//出栈            sta.pop();            insta[w] = 0;            belong[w] = scc;//记录这个节点属于哪个连通分量            num[scc]++;//这个连通分量里面的节点个数加一        }while (w != ver);//直到取到当前节点为止。    }}int main(){    //freopen("input.txt","r",stdin);    while (~scanf("%d %d",&D,&B)){        inittarjan();        init2();        for (int i = 1; i <= B; i++){            int tempx,tempy;            scanf("%d %d",&tempx,&tempy);            if (repeat(tempx,tempy))                addedge(tempx,tempy);        }        memset(used,-1,sizeof(used));        for (int i = 1; i <= D; i++)            if (used[i] == -1){                par[i] = -1;                tarjan(i);            }        //tarjan();        int res = 0;        memset(out,0,sizeof(out));        for (int i = 1; i <= D; i++){//在别的题目里面有可能不是1到D为序号!!            for (int j = head[i]; j != -1;j = pra[j].next){                int u = pra[j].to;                if (belong[pra[j].to] != belong[i]){//判断每个节点是否和子节点属于同一个连通分量,如果不属于。那么这两个连通分量自加1,注意是强连通分量+1不是顶点+1                    out[belong[pra[j].to]]++;                    out[belong[i]]++;                }                //printf("delong[%d] = %d belong[%d] = %d\n",i,belong[i],u,belong[u]);            }        }        //printf("%d\n",scc);        for (int i =1; i <= scc; i++)            if (out[i] == 2){//注意,因为是无向图 所以出度为2就是有向图里的出度为1                //printf("i=%d\n",i);                res++;            }        printf("%d\n",(res+1)/2);    }    return 0;}


0 0
原创粉丝点击