• 图的联通性问题


    图的联通性问题

        ╮(╯▽╰)╭

    一 无向图的联通性问题 

         求割点 桥:

    void dfs1(int u,int f)              // f 存的是 i边  也可以存父亲 
    {
        low[u]=dfn[u]=++tim;
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(dfn[v]==0)
            {   
                dfs1(v,i);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u])                      // >= 求割点  
                {
                    ans=min(bian[i].val,ans);
                    flag[u]=1;
                } 
                    if(low[v]>=dfn[u])                  //   >求桥 
                 {
                    ans=min(bian[i].val,ans);
                    flag[i]=glag[i^1]=1;             // cent初始化为 1   
                 }
            }
            else if((i^1)!=f) low[u]=min(low[u],dfn[v]);   // 可以防止重边问题; 
                 if(v!=f)    low[u]=min(low[u],dfn[v]);   //  存的是父亲 不能防止重边 
        }
    }

        

          点和边的双联通分量的区别: 无穷大1和2在不属于点的双联通分量,属于边双        联通分量

       求点的双联通分量:

        

    void dfs(int u,int fa){
        dfn[u]=low[u]=++tim;
        for(int i=head[u];i;i=bian[i].net) {
            int v=bian[i].to;
            if(!dfn[tv]){
                child++;
                stk[top++]=i;
                dfs(v,u);
                low[u]=min(low[u],low[tv]);
                if(low[tv]>=dfn[u]){
                    flag[u]=true; // 
                    leve++;
                    bcc[leve].clear();
                    while(1){
                        int ii=stk[--top];
                        if(color[bian[ii].to]!=leve){bcc[leve].push_back(bian[ii].to);bccno[bian[ii].to]=leve;}
                        if(color[bian[ii^1].to]!=leve){bcc[leve].push_back(bian[ii^1].to);bccno[bian[ii^1].to]=leve;}
                        if(bian[ii].to==v&&bian[ii^1]==u)break;
                    }
                }
            }
            else if(tv!=fa){
                stk[top++]=i;               // 这个别忘记了 
                low[u]=min(low[u],dfn[tv]);
            }
        }
        if(fa==0&&child==1) flag[u]=0;  // 割点的特判  
    }

       求边的双联通分量:

    void dfs1(int u,int f)     //求桥 
    {
        low[u]=dfn[u]=++tim;
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(dfn[v]==0)
            {
                dfs1(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])
                {
                    flag[i]=flag[i^1]=1;
                }
            }
            else if(v!=f) low[u]=min(low[u],dfn[v]);
        }
    } 
    int leve2=1;
    void dfs2(int u,int leve)   // 标记 
    {
        color[u]=leve;
        vis[u]=1;
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(vis[v]==0)
            {
                
                if(flag[i]==1)
                {  
                     leve2++;
                    dfs2(v,leve2);
                }
                else dfs2(v,leve);
            }
        }
    }

       有向图的强联通

    void dfs1(int u)
    {
        dfn[u]=low[u]=++tim;
        stk[top++]=u;                               // 不一样  想一想  无穷符号 
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(dfn[v]==0)
            {
                dfs1(v);
                low[u]=min(low[u],low[v]);
            }
            else if(color[v]==0) low[u]=min(low[u],dfn[v]); // 不一样的地方 防止到别的环上  不可能到父亲 
        }
        if(dfn[u]==low[u])            // 不一样 
        {
            leve++;
            while(top>0)
            {
                int v=stk[--top];
                color[v]=leve;
                if(v==u) break;
            }
        }
    }

          

      无向图求加多少条边变成整个双联通 

      缩点后 根节点的数量+1除以2;

        for(ri i=1;i<=n;i++)
        for(ri j=head[i];j;j=bian[j].net)
        {
            int v=bian[j].to;
            if(color[v]!=color[i])
             num[color[v]]++;
        }
        for(ri i=1;i<=leve2;i++)
        {   
            if(num[i]==1) ans++;
        } 
        printf("%d\n",(ans+1)/2);

    有向图加多少条边和变成强联通:

     缩点后的入读出度为0 的总的点数量最大的;

             for(ri i=1;i<=n;i++)
            for(ri j=head[i];j;j=bian[j].net)
            {    
                int v=bian[j].to;
                if(color[i]!=color[v])
                {
                    in[color[i]]++;
                    out[color[v]]++;
                }
            }
            for(ri i=1;i<=leve;i++)     
            {
                if(in[i]==0) IN++;
                if(out[i]==0) OUT++;
            }
            if(leve==1) printf("0\n");   // 已经是强联通 
            else
            printf("%d\n",max(IN,OUT));
        }

    题目:

    成为一名骑士是一项非常诱人的职业:寻找圣杯,拯救处于困境的少女以及与其他骑士共饮是一件有趣的事情。因此,近年来亚瑟王王国的骑士人数空前增加,这并不奇怪。现在有这么多的骑士,很少有每个圆桌骑士能够同时来卡梅洛特并坐在圆桌旁的。通常只有一小部分骑士,而其余的骑士则忙着在全国各地做英勇事迹。 

    在讨论过程中,骑士特别容易喝酒,容易引起过度兴奋。在发生一些不幸的事故之后,亚瑟王要求著名的巫师梅林(Merlin)确保以后骑士之间不会发生战斗。在仔细研究了问题之后,Merlin意识到只有按照以下两个规则就座骑士,才能避免打架:
    • 骑士的座位应确保彼此仇恨的两个骑士不应该是餐桌上的邻居。(Merlin列出了谁讨厌谁的名单。)骑士们坐在圆桌旁,因此每个骑士都有两个邻居。
    • 桌子周围应该有奇数个骑士。这样可以确保如果骑士们无法达成共识,那么他们可以通过投票解决问题。(如果骑士的数量是偶数,那么可能会发生“是”和“否”具有相同的投票数,并且这种说法会持续下去。)
    只有满足这两个规则,Merlin才会让骑士坐下,否则他将取消会议。(如果只有一个骑士出现,那么会议也将被取消,因为一个人不能坐在桌子旁。)Merlin意识到,这意味着有些骑士不能参加任何遵守这些规则的座位安排,并且这些骑士将永远无法坐在圆桌会议上(一种情况是,如果一个骑士讨厌其他每个骑士,但还有许多其他可能的原因)。如果骑士不能坐在圆桌会议上,那么他就不能成为圆桌骑士团的成员,必须将其开除。这些骑士必须转移到一个不太负盛名的骑士团,例如方桌骑士,八角桌骑士或香蕉形桌骑士。为了帮助梅林, 

    输入项

    输入包含几个测试用例块。每种情况都从包含两个整数1≤n≤1000和1≤m≤1000000的行开始。数字n是骑士数。接下来的m行描述哪个骑士讨厌哪个骑士。这m行中的每行包含两个整数k1和k2,这意味着骑士数k1和骑士数k2彼此讨厌(数字k1和k2在1到n之间)。 

    输入由n = m = 0的块终止。 

    输出量

    对于每个测试用例,您必须在单独的一行上输出一个整数:必须驱逐的骑士数。 

    样本输入

    5 5
    1 4
    1 5
    2 5
    3 4
    4 5
    0 0
    

    样本输出

    2
    题解 求点的双联通分量,和二分图的判断(二分图一点是个偶图^_^)
    注意 二分图的判断时 不能 return 二分图判断(v,k) 应为儿子还没有访问完,
    代码
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<stack>
    using namespace std;
    const int M =400010;
    #define ri register int
    struct setbian{
        int net,to;
    }bian[M];
    int head[M],cent,arr[1005][1005],low[M],dfn[M],tim,stk[M],top,color[M],f[M],trmp[M],leve;
    vector <int> bcc[M];
    void add(int a,int b)
    {
        bian[++cent].net=head[a],head[a]=cent;
        bian[cent].to=b;
    }
    void dfs1(int r,int f)
    {
        low[r]=dfn[r]=++tim;
        for(ri i=head[r];i;i=bian[i].net)
        {   
            int v=bian[i].to;
            if(dfn[v]==0)
            {  
               stk[top++]=v;
                dfs1(v,r);
                low[r]=min(low[r],low[v]);
                if(low[v]>=dfn[r])
                {
                    leve++;
                    bcc[leve].clear();
                    while(top>0)
                    {
                        int v2=stk[--top];
                         bcc[leve].push_back(v2);
                         if(v==v2) break;
                    }
                    bcc[leve].push_back(r);
                }
            }
            else if(v!=f) low[r]=min(low[r],dfn[v]);
        }
    }
    int dfs2(int r,int num)
    {
        for(ri i=head[r];i;i=bian[i].net)
        {
              int v=bian[i].to;
               if(f[v]!=num)        continue;
               if(color[v]==color[r]) return 0;
               if(color[v]==0)
              {
                   color[v]=3-color[r];
                   if(!dfs2(v,num)) return 0; //  return dfs2(v,num) 这个是错的  
              }
        }
        return 1;
    }
    int n,m;
    int main(){
        while(~scanf("%d%d",&n,&m))
        {
            if(n==0&&m==0) break;
            memset(arr,0,sizeof arr);
            memset(head,0,sizeof head);
            memset(low,0,sizeof low);
            memset(dfn,0,sizeof dfn);
            memset(color,0,sizeof color);
            memset(stk,0,sizeof stk);
            memset(f,0,sizeof f);
            memset(trmp,0,sizeof trmp);
            top=cent=leve=tim=0;
            int a,b;
            for(ri i=1;i<=m;i++)     //
           scanf("%d%d",&a,&b),arr[a][b]=arr[b][a]=1;
            for(ri i=1;i<=n;i++)
            for(ri j=1;j<=n;j++)
            {
                if(i!=j&&arr[i][j]==0)  //
                add(i,j);
            }
            for(ri i=1;i<=n;i++)      //
            {//cout<<"yes"<<endl;
                if(dfn[i]==0) dfs1(i,0);
            }
             for(ri i=1;i<=leve;i++)  //
             {
                 memset(color,0,sizeof color);
                 for (ri j=0;j<bcc[i].size();j++)
                 {
                         int v=bcc[i][j];
                         f[v]=i;
                 }
                 color[bcc[i][0]]=1;
                 if(!dfs2(bcc[i][0],i))   //
                 {   
                     for(ri j=0;j<bcc[i].size();j++)
                     {
                         trmp[bcc[i][j]]=1;
                     }
                 }
             }
             int ans=n;                //
             for(ri i=1;i<=n;i++)
             {
                 if(trmp[i]==1) ans--;
             }
             printf("%d\n",ans);  
        }
        return 0;
    }
    View Code
    
    

     例题 :

    在赤壁战役中,曹操被诸葛亮和周瑜击败。但是他不会放弃。曹操的部队仍不擅长水战,因此他想出了另一个主意。他在长江上建了许多岛屿,在这些岛屿的基础上,曹操的军队可以轻松地攻击周瑜的部队。曹操还修建了连接各岛的桥梁。如果所有岛屿都通过桥梁连接起来,那么曹操的军队可以很方便地在这些岛屿之间部署。周瑜不能忍受,所以他想摧毁一些草桥,以使一个或多个岛屿与其他岛屿分开。但是周瑜只有诸葛亮留下的一颗炸弹,所以他只能摧毁一座桥。周瑜必须派人携带炸弹摧毁这座桥。桥梁上可能会有警卫。轰炸队的士兵人数不得少于桥梁的守卫人数,否则任务将失败。请至少弄清楚周瑜必须派多少士兵来完成离岛任务。

    输入项测试案例不超过12个。 

    在每个测试案例中: 

    第一行包含两个整数N和M,这意味着有N个岛和M个桥。所有岛的编号从1到N。(2 <= N <= 1000,0 <M <= N 2) 

    接下来的M行描述了M个桥。每行包含三个整数U,V和W,这意味着有一个连接孤岛U和孤岛V的桥,并且该桥上有W个防护装置。(U≠V并且0 <= W <= 10,000) 

    输入以N = 0和M = 0结尾。输出量对于每个测试用例,打印出周瑜为完成任务而必须发送的最小士兵人数。如果周瑜无法成功,请打印-1。样本输入

    3 3
    1 2 7
    2 3 4
    3 1 4
    3 2
    1 2 7
    2 3 4
    0 0

    题解 : 求桥,用 i 作为fa 就不用判读重边的问题。直接加进去 ,台秀了╮(╯▽╰)╭
    
    
    
    
    
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAXN =1010;
    #define ri register int 
    struct setbian{
        int net,to,val;
    }bian[MAXN*MAXN+10];
    int head[MAXN],cent=1,tim,low[MAXN],dfn[MAXN],color[MAXN*MAXN+20],leve,num,ans=0x7fffffff;
    void add(int a,int b,int c)
    {
        bian[++cent].net=head[a],head[a]=cent;
        bian[cent].to=b,bian[cent].val=c;
    }
    void dfs1(int u,int f) // f 是边 
    {
        low[u]=dfn[u]=++tim;num++;
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(dfn[v]==0)
            {   
                dfs1(v,i);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])
                {
                    ans=min(bian[i].val,ans);
                }
            }
            else if((i^1)!=f) low[u]=min(low[u],dfn[v]);
        }
    }
    int n,m;
    int main(){
        while(~scanf("%d%d",&n,&m))
        {
             memset(head,0,sizeof head);
             memset(low,0,sizeof low);
             memset(dfn,0,sizeof dfn);
             memset(bian,0,sizeof bian);
             cent=1;ans=0x7fffffff;
                tim=num=0;
            if(n==0&&m==0) break;
            for(ri i=1;i<=m;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
              dfs1(1,-1);
            if(num!=n) {
            printf("0\n");continue;
            }
            if(ans==0x7fffffff){
             printf("-1\n");continue;}
            if(ans==0) printf("1\n");
            else printf("%d\n",ans);
        }
            return 0;
    }
    View Code
    
    
    
     

      

    
    
  • 相关阅读:
    boost::asio 连接管理11 如何关闭连接
    boost::asio async_write也不能保证一次发完所有数据 二
    boost::asio async_write也不能保证一次发完所有数据 一
    boost参考博客
    C++ 多线程编程总结
    Boost::asio io_service 实现分析
    使用boost io_service时,需要注意的东西
    Boost::Thread 多线程的基础知识
    boost::thread类
    Boost::thread库的使用
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/11732277.html
Copyright © 2020-2023  润新知