题目描述

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。

请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

解析

这道题不错,很考验思考问题的全面性,而且代码写起来似乎也比较坑。

咱写挂了(哭)。

乍一看似乎第一问就是要我们求连通块个数,第二问就是要我们求每个连通块中点的个数。

对,但这只是一小部分,我们少考虑了一些东西。

这道题需要分类讨论。

  1. 如果某个连通块一个割点都没有会怎么样?那么在这个子图中,我们在任意两个位置设置出口即可,对于该连通块,若其包含\(x\)个点,其方案数为\(x(x-1)/2\)。
  2. 如果某个连通块只有一个割点,我们在此连通块的任意一点都可以放置出口,并统计方案数。
  3. 特别特别需要注意的一个坑点,我们在考虑设置出口时,要知道此题只会出现一个坍塌的挖煤点所以,如果某个连通块有两个及以上个割点,其还是可以与其它连通块相连,所以这个连通块中是不必设置出口的!

这题最核心的一点就是要注意到只会出现一个坍塌点,所以任何时候我们都只用考虑只有一个割点时的情况。

提一些细节,在dfs划分连通块时,我们不能只记某个点是否到达过,我们得记它属于哪个连通块,否则会少算一些或者是算重复一些(这个还要看你怎么写)。

为什么要提呢,因为我写错了,我甚至还没有注意到只会出现一个坍塌点。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define ll long long
#define N 501
using namespace std;
inline ll read()
{
ll f=1,x=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct rec{
int next,ver;
}g[N*N];
ll head[N],tot,n,m,low[N],dfn[N],cnt,root,c[N],num,k,now;
ll ans1,ans2;
bool cut[N];
ll v[N];
inline void add(int x,int y)
{
g[++tot].ver=y;
g[tot].next=head[x],head[x]=tot;
}
inline void tarjan(int x)//tarjan求割点板子
{
low[x]=dfn[x]=++cnt;
int flag=0;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
flag++;
if(x!=root||flag>1) cut[x]=1;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
inline void dfs(int x)
{
v[x]=num;++k;//划分连通块num,统计该连通块中点的个数k
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(v[y]!=num&&cut[y]) v[y]=num,now++;//这里卡了我许久,这样写才能避免重复计算该连通块包含割点数量
if(!v[y]) dfs(y);
}
}
int main()
{
int kase=0;
while(~scanf("%d",&n)&&n){
memset(head,0,sizeof(head));
memset(cut,0,sizeof(cut));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(v,0,sizeof(v));
tot=ans2=1;ans1=m=0;
for(int i=1;i<=n;++i){
ll u,v;
u=read(),v=read();
add(u,v),add(v,u);
m=max(m,max(v,u));
}
cnt=num=0;
for(int i=1;i<=m;++i)
if(!dfn[i]) root=i,tarjan(i);
for(int i=1;i<=m;++i)
if(!v[i]&&!cut[i]){
k=now=0,num++;
dfs(i);
if(now==0){//注意,这里是重点
ans1+=2;
ans2*=(k-1)*k/2;
}
if(now==1) ans1++,ans2*=k;
}
printf("Case %d: %lld %lld\n",++kase,ans1,ans2);
}
return 0;
}

最新文章

  1. CSS技巧(一):清除浮动
  2. nginx 反向代理
  3. Daily Scrum02 12.07
  4. eclipse启动报错java.lang.IllegalStateException: LifecycleProcessor not initialized - call &#39;refresh&#39; befo
  5. JE22环境安装配置(JDK/ANT/TOMCAT/ECLIPSE)
  6. linux crontab 学习
  7. 6-01T-SQL中的运算符
  8. 关于Linux session管理与GUI架构
  9. android 学习随笔十三(网络:多线程下载)
  10. kthread_stop引起的OOP
  11. cordova,phonegap 重力感应
  12. 基于Bootstrap的jQuery开关按钮组合
  13. xcode 工具 alcatraz---备用
  14. 保存android程序崩溃日志到SD卡
  15. thinkPHP 模板中的语法知识 详细介绍(十二)
  16. linux下安装nginx与配置
  17. StringUtils.isEmpty StringUtils.isBlank
  18. WPF实现特殊统计图
  19. HTML中多种空格转义字符
  20. windows系统下安装tomcat及配置

热门文章

  1. window系统修改时区IIS需要重启
  2. ObjectARX创建带文字的线型实例代码
  3. OpenJudge 4120 硬币
  4. QPS和并发量
  5. 手撕面试官系列(二):开源框架面试题Spring+SpringMVC+MyBatis
  6. [HAOI2015]树上操作 题解
  7. python 之 网络编程(基于TCP协议的套接字通信操作)
  8. 【题解】Luogu P4910 帕秋莉的手环
  9. Layui + thymeleaf org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: &quot; 错误解决
  10. Jekyll自动检测代码更新