zoukankan      html  css  js  c++  java
  • 蓝桥杯历届试题 危险系数(dfs或者并查集求无向图关于两点的割点个数)

    Description

    抗日战争时期,冀中平原的地道战曾发挥重要作用。

    地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

    我们来定义一个危险系数DF(x,y):

    对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。

    本题的任务是:已知网络结构,求两站点之间的危险系数。

    Input

    输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;

    接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;

    最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。

    Output

    一个整数,如果询问的两点不连通则输出-1.

    Sample Input

    7 6
    1 3
    2 3
    3 4
    3 5
    4 5
    5 6
    1 6

    Sample Output

    2

    Source

    蓝桥杯
     
    解法1:DFS
    求关于两个点的割点
    dfs爆搜,找到这两点间的所有路径数目ans,如果在1这些路径中,某些点出现的次数是ans
    那么该点一定是关于目标两点的割点
    具体做法:
    搜索路径,记录路径中每个结点的出现次数,最后与可行路径数比较
    将目标两点设置为起点s和终点f,dfs爆搜其路径
    边搜边记录其路径,搜到终点之后,将记录的路径的结点出现次数加1
    如果在全部搜索完之后,有点在所有可行路径中出现的次数等于可行路径数,那么该点是关于起点和终点和割点
     
    #include<stdio.h>
    #include<iostream>
    #include<math.h>
    #include<string.h>
    #include<set>
    #include<map>
    #include<list>
    #include<queue>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
    int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
    int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
    
    int getval()
    {
        int ret(0);
        char c;
        while((c=getchar())==' '||c=='
    '||c=='
    ');
        ret=c-'0';
        while((c=getchar())!=' '&&c!='
    '&&c!='
    ')
            ret=ret*10+c-'0';
        return ret;
    }
    
    #define max_v 1005
    int vis[max_v];
    int cnt[max_v];
    int path[max_v];
    int G[max_v][max_v];
    int n,m;
    int ans;
    int s,f;
    
    void init()
    {
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        memset(path,0,sizeof(path));
        memset(G,0,sizeof(G));
        ans=0;
    }
    void dfs(int cur,int len)
    {
        if(cur==f)
        {
            ans++;
            for(int i=1;i<len;i++)
                cnt[path[i]]++;
            return ;
        }
        for(int i=1;i<=n;i++)
        {
            if(G[cur][i]&&vis[i]==0)
            {
               vis[i]=1;
               path[len]=i;
    
               dfs(i,len+1);
    
               vis[i]=0;
            }
        }
    }
    int main()
    {
        int x,y;
        scanf("%d %d",&n,&m);
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&x,&y);
            G[x][y]=G[y][x]=1;
        }
        scanf("%d %d",&s,&f);
    
        vis[s]=1;
        path[1]=s;
    
        dfs(s,2);
    
        if(ans==0)
        {
            printf("-1
    ");
            return 0;
        }
        int sum=0;
        for(int i=1;i<=n;i++)
            if(cnt[i]==ans)
               sum++;
        printf("%d
    ",sum-2);
        return 0;
    }
    /*
    求关于两个点的割点
    dfs爆搜,找到这两点间的所有路径数目ans,如果在1这些路径中,某些点出现的次数是ans
    那么该点一定是关于目标两点的割点
    
    具体做法:
    搜索路径,记录路径中每个结点的出现次数,最后与可行路径数比较
    
    将目标两点设置为起点s和终点f,dfs爆搜其路径
    边搜边记录其路径,搜到终点之后,将记录的路径的结点出现次数加1
    如果在全部搜索完之后,有点在所有可行路径中出现的次数等于可行路径数,那么该点是关于起点和终点和割点
    
    
    */

    解法二:并查集

    无向图求关于某两点的割点个数
    遍历每个点,进行多次并查集操作,如果去除和某个点有关的所有边
    导致起点和终点不能连通,那么该点属于关于这两点的割点
    该方法属于暴力法

    #include<stdio.h>
    #include<iostream>
    #include<math.h>
    #include<string.h>
    #include<set>
    #include<map>
    #include<list>
    #include<queue>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
    int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
    int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
    
    int getval()
    {
        int ret(0);
        char c;
        while((c=getchar())==' '||c=='
    '||c=='
    ');
        ret=c-'0';
        while((c=getchar())!=' '&&c!='
    '&&c!='
    ')
            ret=ret*10+c-'0';
        return ret;
    }
    
    #define max_v 1005
    int pa[max_v];
    int n,m;
    int a[max_v],b[max_v];
    int s,f;
    void init()
    {
        for(int i=1;i<=n;i++)
            pa[i]=i;
    }
    int find_set(int x)
    {
        if(x!=pa[x])
            pa[x]=find_set(pa[x]);
        return pa[x];
    }
    void union_set(int x,int y)
    {
        x=find_set(x);
        y=find_set(y);
        if(x==y)
            return ;
        pa[x]=y;
    }
    int fun(int v)
    {
        init();
        for(int i=1;i<=m;i++)
        {
            if(a[i]==v||b[i]==v)
                continue;
            union_set(a[i],b[i]);
        }
        if(find_set(s)!=find_set(f))
            return 0;
        else
            return 1;
    }
    int main()
    {
        int x,y;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&x,&y);
            a[i]=x;
            b[i]=y;
        }
        scanf("%d %d",&s,&f);
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            if(i==s||i==f)
                continue;
           if(fun(i)==0)
            sum++;
        }
        printf("%d
    ",sum);
        return 0;
    }
    /*
    无向图求关于某两点的割点个数
    遍历每个点,进行多次并查集操作,如果去除和某个点有关的所有边
    导致起点和终点不能连通,那么该点属于关于这两点的割点
    该方法属于暴力法
    */
  • 相关阅读:
    好用,Office超效率速成技
    Oracle 12c从入门到精通:视频教学超值版
    用Excel学数据分析
    VMware、Citrix和Microsoft虚拟化技术详解与应用实践
    中文版Dreamweaver CC+Flash CC+Photoshop CC网页设计基础培训教程(新编实战型全功能培训教材)
    1467.二叉排序树
    1177.查找
    1178.复数集合
    1165.字符串匹配
    1166.迭代求立方根
  • 原文地址:https://www.cnblogs.com/yinbiao/p/9856819.html
Copyright © 2011-2022 走看看