Mining Your Own Business UVA - 1108 (点-双连通分量)

题目链接:https://vjudge.net/problem/UVA-1108

题意:有一座地下的稀有金属矿由n条隧道和一些连接点组成,其中每条隧道连接两个连接点。任意两个连接点之间最多只有一条隧道。为了降低矿工的危险,你的任务是在一些连接点处安装太平井和相应的逃生装置,使得不管哪个连接点倒塌,不在此连接点的所有矿工都能到达太平井逃生(假定除了倒塌的连接点外,其他隧道和连接点完好无损)。为了节约成本,你应当在尽量少的连接点安装太平井。还要需要计算出太平井的数目最小时的安装方案总数。

分析:本题的模型是:在一个无向图上选择尽量少的点涂黑(对应太平井),使得任意删除一个点后,每个连通分量至少有一个黑点。不难发现,把割顶涂黑是不划算的。进一步分析,当一个点-双连通分量只有一个割顶时才需要涂,而且是任选一个非割顶涂黑即可。两个问题同时解决。
一个特殊情况是整个图没有割顶。此时需要涂两个点,方案总数是V*(V-1)/2,其中V是连接点的个数。

代码变量名看不懂的话可以参考这篇博客的变量名注释:https://blog.csdn.net/qq_36300700/article/details/81188107

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <string.h>
#include <stack>


using namespace std;

const int maxn =50000+10;

int n,m;
int pre[maxn],iscut[maxn],dfs_clock,bcc_cnt,bccno[maxn];
vector <int> G[maxn],bcc[maxn];

void init()
{
    dfs_clock=bcc_cnt=0;
   memset(pre,0,sizeof(pre));
   memset(iscut,0,sizeof(iscut));
   memset(bccno,0,sizeof(bccno));
    for(int i=1; i<maxn; i++)
        G[i].clear();
}

struct Edge
{
    int u,v;
    Edge(int u, int v):u(u),v(v){};
};

stack<Edge> S;

int dfs(int u,int fa)
{
    int lowu = pre[u] =++dfs_clock;
    int child =0;
    for(int i=0; i<G[u].size(); i++)
    {
        int v= G[u][i];
        Edge e = (Edge){u,v};
        if(!pre[v])
        {
            S.push(e);
            child++;
            int lowv = dfs(v,u);
            lowu=min(lowu,lowv);
            if(lowv >= pre[u])
            {
                iscut[u]=1;
                bcc_cnt++;
                bcc[bcc_cnt].clear();

                for(; ;)
                {
                   Edge x=S.top();
                   S.pop();
                   if(bccno[x.u]!=bcc_cnt)
                   {
                       bcc[bcc_cnt].push_back(x.u);
                       bccno[x.u]=bcc_cnt;
                   }

                   if(bccno[x.v]!=bcc_cnt)
                   {
                       bcc[bcc_cnt].push_back(x.v);
                       bccno[x.v]=bcc_cnt;
                   }

                   if(x.u==u&&x.v==v)
                    break;
                }
            }
        }
        else if(pre[v]<pre[u]&&v!=fa)
        {
            S.push(e);
            lowu = min(lowu,pre[v]);
        }
    }
    if(fa<0&&child==1)
        iscut[u]=0;
    return lowu;
}

int main()
{
    int kase = 0;
    while(scanf("%d",&m)&&m)
    {
        init();
        int u,v;
        n=-1;
        for(int i=0; i<m; i++)
        {
            scanf("%d %d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
            n=max(n,max(u,v));
        }

        for(int i=1; i<=n; i++)
        {
            if(!pre[i])
                dfs(i,-1);
        }

    long long ans1=0,ans2=1;

        for(int i=1; i<=bcc_cnt; i++)
        {
            int cut_cnt =0;
            for(int j=0; j<bcc[i].size(); j++)
                {
                    if(iscut[bcc[i][j]])
                    cut_cnt++;
                }

            if(cut_cnt==1)
            {
                ans1++;
                ans2*=(long long )(bcc[i].size()-cut_cnt);
            }
        }
        if(bcc_cnt==1)
        {
            ans1=2; ans2=bcc[1].size()*(bcc[1].size()-1)/2;
        }
        printf("Case %d: %lld %lld\n",++kase,ans1,ans2);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值