您的位置:首页 > 其它

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

2015-03-25 09:00 351 查看
用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){//遍历子节点。如果子节点没有被访问过,则dfs

par[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 50005
using 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){//遍历子节点。如果子节点没有被访问过,则dfs

par[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  边双连通分量 poj