zoukankan      html  css  js  c++  java
  • [HNOI2012]矿场搭建

    Description

    煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

    Input

    输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖       S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。

    Output

    输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。

    Sample Input

    9
    1 3
    4 1
    3 5
    1 2
    2 6
    1 5
    6 3
    1 6
    3 2
    6
    1 2
    1 3
    2 4
    2 5
    3 6
    3 7
    0

    Sample Output

    Case 1: 2 4
    Case 2: 4 1

    HINT

    Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);

    Case 2 的一组解为(4,5,6,7)。

    题解:

    1.这道题牵涉到我们省选以前很少用过的东西——双连通分量。

    这题就和点双连通有关,回顾一下点双连通。

    定义:在一个点双连通图中,任意连个不同的点都至少有两条不经过同一个点的路径将它们相连。

    从定义中我们可以看出,在一个点双连通图中,去掉任意一个点,其它的点都是联通的。也就是说,对于一个点双连通图,我们要建两个出口,那么即使一个出口坍塌,我们还可以从另外一个出口出去。

     

    2.这只是一个很笼统的想法,因为题目所给的图不一定是点双连通图。但是可以肯定的是,任何一个图都是由很多个点双连通组成的,而且点双连通两两之间都是用割点链接起来的(如果是桥,那么就是两个割点),也就是说我们可以把所给的图拆分成很多个点双连通。

     

    3.那么拆分出来的点双连通都会和0个,1个,2个,或者更多个点双联通分量相连(也就是和割点相连)。下面我们来分类讨论:

           Zero 当和0个割点相连时,如上文所说,要建两个出口。

           One 当和1个割点相连时,要建1个出口,若果割点炸了,那么还可以从这个出口出去。

           Two or more 当和2个或两个以上个割点相连时,不需要建出口,因为一个割点炸了,还可以从另外一个割点到达别的点双联通分量。

     

    4.对于题目第二问求方案数,直接用乘法原理就可以了。这一问很简单,自己YY一下就好。

      1 //Never forget why you start
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<cstdlib>
      5 #include<cstring>
      6 #include<cmath>
      7 #include<algorithm>
      8 using namespace std;
      9 typedef long long lol;
     10 int n,m,t,ans;
     11 lol ans2;
     12 struct node {
     13     int next,to;
     14 } edge[1005];
     15 int head[505],size=0;
     16 void putin(int from,int to) {
     17     size++;
     18     edge[size].next=head[from];
     19     edge[size].to=to;
     20     head[from]=size;
     21 }
     22 void clean();
     23 int dfn[505],low[505],iscut[505],dfscnt,root,child;
     24 void tarjan(int r,int fa) {
     25     int i;
     26     dfn[r]=low[r]=++dfscnt;
     27     for(i=head[r]; i!=-1; i=edge[i].next) {
     28         int y=edge[i].to;
     29         if(!dfn[y]) {
     30             tarjan(y,r);
     31             low[r]=min(low[r],low[y]);
     32             if(low[y]>=dfn[r]) {
     33                 if(r!=root)iscut[r]=1;
     34                 else child++;
     35             }
     36         } else if(y!=fa)low[r]=min(low[r],dfn[y]);
     37     }
     38 }//tarjan求割点 
     39 int cnt,vis[505],num,sum;
     40 void dfs(int r) {
     41     int i;
     42     vis[r]=cnt;
     43     num++;
     44     for(i=head[r]; i!=-1; i=edge[i].next) {
     45         int y=edge[i].to;
     46         if(vis[y]!=cnt&&iscut[y]) {
     47             sum++;
     48             vis[y]=cnt;
     49         } else if(vis[y]!=cnt)dfs(y);
     50     }
     51 }//遍历每个点双连通分量,sum表示和这个点双联通分量相连的割点的个数 
     52 int main() {
     53     int i,j;
     54     while(scanf("%d",&m)!=EOF) {//多组数据 
     55         if(m==0)break;
     56         clean();//各种初始化 
     57         t++;
     58         for(i=1; i<=m; i++) {
     59             int u,v;
     60             scanf("%d%d",&u,&v);
     61             putin(u,v);
     62             putin(v,u);
     63             n=max(n,v);
     64             n=max(n,u);
     65         }
     66         for(i=1; i<=n; i++) {
     67             if(!dfn[i]) {
     68                 root=i;
     69                 child=0;
     70                 tarjan(i,i);
     71                 if(child>=2)iscut[root]=1;
     72             }
     73         }
     74         for(i=1; i<=n; i++) {
     75             if(!vis[i]&&!iscut[i]) {
     76                 cnt++;
     77                 num=0;
     78                 sum=0;
     79                 dfs(i);//遍历每一个点双连通分量 
     80                 if(sum==0) {
     81                     ans+=2;
     82                     ans2*=num*(num-1)/2;
     83                 }
     84                 if(sum==1) {
     85                     ans+=1;
     86                     ans2*=num;//统计答案 
     87                 }
     88             }
     89         }
     90         printf("Case %d: %d %lld
    ",t,ans,ans2);
     91     }
     92     return 0;
     93 }
     94 void clean() {
     95     memset(head,-1,sizeof(head));
     96     memset(dfn,0,sizeof(dfn));
     97     memset(low,0,sizeof(low));
     98     memset(iscut,0,sizeof(iscut));
     99     memset(vis,0,sizeof(vis));
    100     size=0;
    101     dfscnt=0;
    102     cnt=1;
    103     ans=0;
    104     n=0;
    105     ans2=1;
    106 }//各种初始化 
  • 相关阅读:
    ansible——playbook conditions条件判断
    ansible——playbook lookups从插件加载变量
    ansible——playbook循环
    lombok注解
    集合与集合取笛卡尔积
    List排列组合
    synchronized初识
    java IO与NIO
    文件I/O和标准I/O
    双数据源配置
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/8260255.html
Copyright © 2011-2022 走看看