蓝桥杯 历届试题 危险系数 两点间割点法 By Assassin

来源:互联网 发布:宿州淘宝xinsuzhou 编辑:程序博客网 时间:2024/05/21 10:24

这是危险系数题目继暴力法的深入的学习
暴力法见我的博客
here

首先我们除了暴力法还有什么方法呢?用割点,但是这里还有一些小的变形,比如说需要注意是两个点经过的路径上的割点数量。我这里尝试使用vector数组建图然后完成割点算法,中间还有很多需要注意的问题,我全部写在代码中了,还劳烦各位直接看代码。割点的学习给大家推荐这个博客:

一篇很不错的割点算法讲解

然后给大家简单的几个样例

  • 样例1
    7 7
    1 2
    1 3
    2 4
    2 5
    3 6
    3 7
    1 7
    1 7
    ans=0
    这里写图片描述

  • 样例2

7 6
1 2
1 3
2 4
2 5
3 6
3 7
1 7
ans=1

这里写图片描述

  • 样例3
    7 6
    1 3
    2 3
    3 4
    3 5
    4 5
    5 6
    1 6
    ans=2(3、5)

这里写图片描述

下面上我的丑代码,希望帮助到大家(蓝桥杯还是推荐用最暴力的方法)

#include<bits/stdc++.h>#define input freopen("input.txt","r",stdin)using namespace std;vector<int>vec[1005];               //用vec建无向图 int n,m,start,end;                  //start和end记录两个关键起始点 int low[1005],dfn[1005],vis[1005],endishere[1005];      //用割点算法,dfn分别访问顺序,low和为 dfs搜索树当前节点及其子树返回的最早的位置,endishere代表从start到end中间经过的位置 const int inf=0x3f3f;               //初始low变量 int pos;                            //全局变量访问顺序 void dfs(int now,int from){         //now表示现在的节点,from表示从哪个节点过来的,用来避免父节点回环,如1->3->1     dfn[now]=pos;                   //访问顺序     pos++;                              vis[now]=1;                     //标记访问过这一点     for(int i=0;i<vec[now].size();i++){     //遍历和当前节点链接的几个点         if(vis[vec[now][i]]==1&&dfn[now]>dfn[vec[now][i]]&&vec[now][i]!=from){            //三个条件代表 1.子节点被访问过 2.当前节点访问顺序数大于子节点的(意味着是算法中的回边) 3.不是直接回父节点              low[now]=min(low[now],dfn[vec[now][i]]);             // 割点算法中的 ,当前回访最小值 是否更新为返回位置         }        else if(vec[now][i]!=from&&vis[vec[now][i]]==0){ //1.不是父节点来的 2.子节点未被访问过             dfs(vec[now][i],now);                        //先搜索后更新low的值,注意体会一下,这样就能做到从叶子节点到根的更新             if(low[vec[now][i]]<dfn[now]){               //事实上只有子树到达访问位置比自身的访问位置还小的时候才是有意义的                 low[now]=min(low[now],low[vec[now][i]]); //算法中树边的更新             }            if(vec[now][i]==end||endishere[vec[now][i]]==1){    //记录start和end中经过的点,如果子树是end或者子树已经为1则标1                 endishere[now]=1;                               //因为刚刚的先搜索再标记所以可行             }        }    }}int main(){    input;    int tmp1,tmp2,i,j;    while(scanf("%d%d",&n,&m)!=EOF){        for(i=1;i<=n;i++){            low[i]=inf;        }        memset(dfn,0,sizeof(dfn));        memset(vis,0,sizeof(vis));        memset(endishere,0,sizeof(endishere));        for(i=1;i<=n;i++){            vec[i].clear();        }        for(i=1;i<=m;i++){            scanf("%d%d",&tmp1,&tmp2);            vec[tmp1].push_back(tmp2);      //用vec建立无向图             vec[tmp2].push_back(tmp1);        }         scanf("%d%d",&start,&end);        int ans=0;                          //答案的变量         pos=1;                              //初始化访问顺序数         dfs(start,0);                       //从start进行dfs搜索树的搜索         endishere[end]=1;                   //标记结束点         for(i=1;i<=n;i++){            if(i!=start&&vec[i].size()!=1&&endishere[i]==1){                //1.此点不是出发点 2.此点不是叶子节点,叶子节点是不可能为割点的 3.为了保证在两点之间,所以endisshere标记为1                 int flag=0;                for(j=0;j<vec[i].size();j++){   //这是为了找当前节点的在‘路上’的子节点是否存在不可以返回更早的点                     if(low[vec[i][j]]>=dfn[i]&&dfn[vec[i][j]]>dfn[i]&&endishere[vec[i][j]]==1){                        //1.不能返回更早的点 2.确定子节点是当前节点的子节点(因为是无向图不存在高低之说,需要从访问顺序判断) 3.在标记的路上                         flag=1;                                         break;                    }                }                if(flag==1){                    ans++;                }            }        }        cout<<ans<<endl;        //答案     }    return 0;}
1 0