POJ3352 桥,边双连通分量,构造边双连通图

来源:互联网 发布:mac os 10.12镜像下载 编辑:程序博客网 时间:2024/05/16 07:07

虽然题目保证没有重边,但是代码适用于有重边的情况,另觉得网上流传的通过LOW值相等判断两点属于同一边双连通分量不对,可举出反例,本代码做法是利用tarjan求桥,删桥,缩点,(leaf+1)/2。

代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<stack>#include<fstream>using namespace std;#define maxv 1005struct Edge{    int T,Reverse;    bool Mark;    Edge()    {}    Edge(int t,int r)    {        T=t,Reverse=r;        Mark=false;    }};struct Bridge{    int Vertex,Index;    Bridge()    {}    Bridge(int u,int i)    {        Vertex=u,Index=i;    }};vector<Edge> G[maxv];vector<Bridge> BRI;int DFN[maxv],LOW[maxv],DEG[maxv],BEL[maxv];int IND,CNT;void Init(){    int i;    for(i=0;i<maxv;++i)        G[i].clear();    BRI.clear();    memset(DFN,0,sizeof(DFN));    memset(LOW,0,sizeof(LOW));    memset(DEG,0,sizeof(DEG));    memset(BEL,0,sizeof(BEL));    IND=0,CNT=0;}void Tarjan(int u){    DFN[u]=LOW[u]=++IND;    int i,v;    for(i=0;i<G[u].size();++i)    {        if(G[u][i].Mark)            continue;        v=G[u][i].T;        G[u][i].Mark=true;        G[v][G[u][i].Reverse].Mark=true;        if(DFN[v]==0)        {            Tarjan(v);            LOW[u]=min(LOW[u],LOW[v]);            if(DFN[u]<LOW[v])                BRI.push_back(Bridge(u,i));        }        else            LOW[u]=min(LOW[u],DFN[v]);    }}void Part(int u){    BEL[u]=CNT;    int i,v;    for(i=0;i<G[u].size();++i)    {        if(G[u][i].Mark==false)            continue;        v=G[u][i].T;        G[u][i].Mark=false;        G[v][G[u][i].Reverse].Mark=false;        if(BEL[v]!=0)            continue;        Part(v);    }}int Solve(int n){    int i,u,v,ind,leaf=0;    Tarjan(1); //因为题目已经保证一次深搜即可遍历所有顶点了//    cout<<"Part 1 end"<<endl;     已经求出所有的桥     for(i=0;i<BRI.size();++i)    {        u=BRI[i].Vertex,ind=BRI[i].Index;        G[u][ind].Mark=false;        v=G[u][ind].T;        ind=G[u][ind].Reverse;        G[v][ind].Mark=false;    }//    cout<<"Part 2 end"<<endl;    通过标记,删去桥     for(i=1;i<=n;++i)        if(BEL[i]==0)        {            CNT++;            Part(i);        }//    cout<<"Part 3 end"<<endl;    求出边双连通分量     for(i=0;i<BRI.size();++i)    {        u=BRI[i].Vertex,ind=BRI[i].Index;        v=G[u][ind].T;        DEG[BEL[u]]++;        DEG[BEL[v]]++;    }//    cout<<"Part 4 end"<<endl;    //求度数 无相图任意两个双连通分量只能有一条边相连,不能有重边,否则就可以变成一个双连通分量 //因此可以通过枚举桥来求度数     for(i=1;i<=CNT;++i)        if(DEG[i]==1)            leaf++;//    cout<<"Part 5 end"<<endl;    return (leaf+1)/2;}       int main(){    int n,m,u,v,tmp,r1,r2;//    ifstream fin("data.txt");//    fin>>n>>m;    scanf("%d %d",&n,&m);    Init();    while(m--)    {//        fin>>u>>v;        scanf("%d %d",&u,&v);        r1=G[v].size();        r2=G[u].size();        G[u].push_back(Edge(v,r1));        G[v].push_back(Edge(u,r2));    }    cout<<Solve(n)<<endl;//    system("pause");    return 0;}