• POJ 3352 Road Construction


    题意就是至少加几条边,能够让原图没有桥。

    1. 首先要找出图G的所有【边双连通分量】。

    2、  把每一个【边双连通分量】都看做一个点(即【缩点】)

    3、  问题再次被转化为“至少在缩点树上增加多少条树边,使得这棵树变为一个双连通图”。

    首先知道一条等式:

    若要使得任意一棵树,在增加若干条边后,变成一个双连通图,那么

    至少增加的边数 =( 这棵树总度数为1的结点数 + 1 )/ 2

    那么我们只需求缩点树中总度数为1的结点数(即叶子数)有多少就可以了。换而言之,我们只需求出所有缩点的度数,然后判断度数为1的缩点有几个,问题就解决了。

    4、  求出所有缩点的度数的方法

    两两枚举图G的直接连通的点,只要这两个点不在同一个【缩点】中,那么它们各自所在的【缩点】的度数都+1。注意由于图G时无向图,这样做会使得所有【缩点】的度数都是真实度数的2倍,必须除2后再判断叶子。

    用了我的宇宙无敌超级大模版,1A了。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    const int maxn=10000+10; //结点数量
    const int Maxn=2*100000+10; //边的数量
    int low[maxn];
    int dfn[maxn];
    int U[maxn],V[maxn];//存初始边
    int flag[maxn];//判断第i条边是不是割边
    int iscut[maxn];//判断i结点是不是割点,去掉之后有几个连通分量
    struct Edge
    {
        int from,to,id,ans;//ans为1,表示这条边是割边
    } edge[Maxn];
    vector<int>G[maxn];//邻接表
    int N,M;//N个结点,M条边
    int tmpdfn;//时间戳
    int tot;
    int son;
    int Start,End;
    
    //以下是输出点双连通分量用的
    int top;
    struct Printf_Egde
    {
        int u,v,id;
        void output()
        {printf("(%d,%d) ",u,v);}
    };
    Printf_Egde Stack[Maxn];
    int Flag[Maxn];
    
    int TxT[maxn];//求边双连通分量用的
    
    //以下是缩点
    vector<int>SD[maxn];
    int FFLAG[maxn];
    int JiHe[maxn];
    int BianHao;
    int RuDu[maxn];
    
    void init()
    {
        for(int i=0; i<maxn; i++) G[i].clear();
        for(int i=0; i<maxn; i++) SD[i].clear();
        memset(JiHe,-1,sizeof(JiHe));
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(iscut,0,sizeof(iscut));
        memset(Flag,0,sizeof(Flag));
        memset(flag,0,sizeof(flag));
        memset(TxT,0,sizeof(TxT));
        memset(FFLAG,0,sizeof(FFLAG));
        memset(RuDu,0,sizeof(RuDu));
        low[1]=dfn[1]=1;
        tmpdfn=0;
        tot=0;
        son=0;
        top=-1;
    
        BianHao=1;
    }
    
    void AddEdge(int u,int v)
    {
        edge[tot].from=u;
        edge[tot].to=v;
        edge[tot].id=tot;
        edge[tot].ans=0;
        G[u].push_back(tot);
        tot++;
    
        edge[tot].from=v;
        edge[tot].to=u;
        edge[tot].id=tot;
        edge[tot].ans=0;
        G[v].push_back(tot);
        tot++;
    }
    
    int Tarjan(int u,int id)
    {
        tmpdfn++;
        int lowu=dfn[u]=tmpdfn;
        for(int i=0; i<G[u].size(); i++)
        {
            int B=G[u][i];
    
            Printf_Egde t;
            if(!Flag[edge[B].id/2])//没有入过栈
            {
                Flag[edge[B].id/2]=1;
                t.u=u;
                t.v=edge[B].to;
                t.id=edge[B].id/2;
                Stack[++top]=t;
            }
            if(!dfn[edge[B].to])
            {
                int lowv=Tarjan(edge[B].to,edge[B].id);
                lowu=min(lowu,lowv);
                if(lowv>=dfn[u])
                {
                    if(u!=1) iscut[u]++;
                    if(u==1) son++;
    
                    /*
                    //输出点双连通分量
                    printf("点双连通分量:");
                    while(1)
                    {
                        if(top==-1) break;
                        Printf_Egde t1;
                        t1=Stack[top];
                        t1.output();
                        top--;
                        if(t1.id==t.id) break;
                    }
                    printf("
    ");
                    */
                    //判断是不是割边
                    if(lowv>dfn[u])
                        edge[B].ans=1;
                }
            }
            else if(dfn[edge[B].to])
            {
                if(edge[B].id/2==id/2) continue;
                lowu=min(lowu,dfn[edge[B].to]);
            }
        }
    
        low[u]=lowu;
        return lowu;
    }
    
    void Display_Cutting_edge()
    {
        for(int i=0; i<2*M; i++)
            if(edge[i].ans)
            {
                FFLAG[i]=1;
                //printf("第%d条边是割边:(%d,%d)
    ",edge[i].id/2,edge[i].from,edge[i].to);
            }
    
    }
    
    void Display_Cutting_point()
    {
        if(son>1) iscut[1]=son-1;
        for(int i=Start;i<=End;i++)
            if(iscut[i]){}
                //printf("编号为%d的结点是割点,删除后有%d个连通分量
    ",i,iscut[i]+1);
    }
    
    void Dfs(int x,int y)
    {
        int XZ=0;
        for(int i=0;i<G[x].size();i++)
        {
            int B=G[x][i];
            if(!flag[edge[B].id/2])
            {
                XZ=1;
                flag[edge[B].id/2]=1;
                TxT[edge[B].to]=1;
               // printf("(%d,%d) ",edge[B].from,edge[B].to);
    
                if(JiHe[edge[B].from]==-1&&JiHe[edge[B].to]==-1)
                {
                    JiHe[edge[B].from]=BianHao;
                    JiHe[edge[B].to]=BianHao;
                    BianHao++;
                }
                else if(JiHe[edge[B].from]!=-1)
                    JiHe[edge[B].to]=JiHe[edge[B].from];
                else if(JiHe[edge[B].to]!=-1)
                    JiHe[edge[B].from]=JiHe[edge[B].to];
    
                Dfs(edge[B].to,y+1);
            }
        }
        if(!XZ&&!y)
        {
    
            //printf("(%d) ",x);
            if(JiHe[x]==-1)
            {
                JiHe[x]=BianHao;
                BianHao++;
            }
        }
    }
    
    void Slove()
    {
        //把桥都标为1
        for(int i=0; i<2*M; i++)
            if(edge[i].ans)
                flag[edge[i].id/2]=1;
    
        for(int i=Start;i<=End;i++)
        {
            if(!TxT[i])
            {
                TxT[i]=1;
               // printf("边双连通分量:");
                Dfs(i,0);
               // printf("
    ");
            }
        }
    }
    
    int main()
    {
        while(~scanf("%d%d",&N,&M)){
        init();
        for(int i=0; i<M; i++)
        {
            scanf("%d%d",&U[i],&V[i]);
            AddEdge(U[i],V[i]);
        }
    
        //设置结点编号的起点和终点
        Start=1;
        End=N;
    
        Tarjan(1,-1);
    
        //割点的输出
        Display_Cutting_point();
    
        //割边的输出
        Display_Cutting_edge();
    
        //点双连通分量在Tarjan过程中已经输出了
    
        //求边双连通分量,并输出
        Slove();
    
        for(int i=0;i<2*M;i++)
        {
            if(JiHe[edge[i].from]!=JiHe[edge[i].to])
            {
                RuDu[JiHe[edge[i].from]]++;
                RuDu[JiHe[edge[i].to]]++;
            }
        }
        int AAns=0;
        for(int i=1;i<BianHao;i++)
        {
            RuDu[i]=RuDu[i]/2;
            if(RuDu[i]==1) AAns++;
        }
        printf("%d
    ",(AAns+1)/2);}
        return 0;
    }
  • 相关阅读:
    What does the LayoutAwarePage Class provide
    UML类图几种关系的总结
    WriteableBitmap 巧学巧用
    iOS图片填充UIImageView(contentMode)
    UIImage扩展用代码直接改变图片大小
    iOS各框架功能简述以及系统层次结构简单分析
    【深拷贝VS浅拷贝】
    【node】node连接mongodb操作数据库
    【node】mongoose的基本使用
    【node】websocket
  • 原文地址:https://www.cnblogs.com/zufezzt/p/4722538.html
Copyright © 2020-2023  润新知