题目链接:http://vjudge.net/problem/viewProblem.action?id=14877
题目大意:
要尽可能多的烧毁桥,另外还要保证图的连通性,问哪些桥是绝对不能烧毁的
我们很容易看出不能烧毁的是必然是作为割边存在的桥。
求割边,我们用Tarjan算法,这与求割点有点小区别在与,对于(u,v)的点low[v]>=dfn[u]时就表示u为割点,而low[v]>dfn[u]时才能说明(u,v)是一条割边
因为这里要求出割边的序号,所以在写边的结构体时,用id代表桥的序号,我们每次得到a,b总会添加两条边a->b和b->a,因为这是无向图,所以这两条边公用一个id
另外要注意的是这道题目允许两个地点有多条边出现,所以我们需要用一个tag标志位来注明是否有重边
oid addPath(int a,int b,int c)
{
int i;
for(i=first[a];i!=-1;i=path[i].next)
if(path[i].y==b) break;
if(i!=-1)//说明是重边
path[i].tag=1;
else{
path[k].y=b,path[k].tag=0,path[k].next=first[a],path[k].id=c;
first[a]=k;
k++;
}
}
每次深度搜索一个节点,不断更新上面的low值和dfn值,并找到low[v]>dfn[u]的边并将它们保存到bridge数组中,nbridge用来统计桥的数量
void dfs(int u,int fa)
{
visit[u]=1,dfn[u]=low[u]=tmpdfn++;
for(int i=first[u];i!=-1;i=path[i].next){
int j=path[i].y;
if(!visit[j]){
dfs(j,u);
low[u]=min(low[j],low[u]);
if(low[j]>dfn[u]&&!path[i].tag)
bridge[++nbridge]=path[i].id;
}
else{
if(j!=fa) low[u]=min(low[u],dfn[j]);//j已被访问且不是父亲节点,说明可以形成一条回边
}
}
}
总代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 10005 #define M 100005 int tmpdfn,k,nbridge,bridge[M],visit[N],dfn[N],low[N]; int first[N]; struct Path{ int y,tag,id; int next; }path[2*M]; void addPath(int a,int b,int c) { int i; for(i=first[a];i!=-1;i=path[i].next) if(path[i].y==b) break; if(i!=-1)//说明是重边 path[i].tag=1; else{ path[k].y=b,path[k].tag=0,path[k].next=first[a],path[k].id=c; first[a]=k; k++; } } void dfs(int u,int fa) { visit[u]=1,dfn[u]=low[u]=tmpdfn++; for(int i=first[u];i!=-1;i=path[i].next){ int j=path[i].y; if(!visit[j]){ dfs(j,u); low[u]=min(low[j],low[u]); if(low[j]>dfn[u]&&!path[i].tag) bridge[++nbridge]=path[i].id; } else{ if(j!=fa) low[u]=min(low[u],dfn[j]);//j已被访问且不是父亲节点,说明可以形成一条回边 } } } int main() { int T,n,m,x,y; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); k=0,nbridge=0,tmpdfn=1; memset(first,-1,sizeof(first)); memset(visit,0,sizeof(visit)); memset(bridge,0,sizeof(bridge)); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); addPath(x,y,i); addPath(y,x,i); } dfs(1,0); printf("%d\n",nbridge); sort(bridge+1,bridge+nbridge+1); for(int i=1;i<nbridge;i++) printf("%d ",bridge[i]); if(nbridge>0) printf("%d\n",bridge[nbridge]); if(T>0) printf("\n"); } return 0; }
ZOJ 2588 求割边问题