题目链接
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。
为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。
于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入格式
输入文件有若干组数据,每组数据的第一行是一个正整数 N,表示工地的隧道数。
接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖煤点 S 与挖煤点 T 由隧道直接连接。
注意,每组数据的挖煤点的编号为 1∼Max,其中 Max 表示由隧道连接的挖煤点中,编号最大的挖煤点的编号,可能存在没有被隧道连接的挖煤点。
输入数据以 0 结尾。
输出格式
每组数据输出结果占一行。
其中第 i 行以 Case i:
开始(注意大小写,Case
与 i
之间有空格,i
与 :
之间无空格,:
之后有空格)。
其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总数。
输入数据保证答案小于 264,输出格式参照以下输入输出样例。
数据范围
1≤N≤500,
1≤Max≤1000
输入样例:
输出样例:
解题思路
缩点,点双连通分量
针对割点的缩点,是指将所有割点先独立起来,然后将所有的点双连通分量(其包含若干个割点)向对应的割点建边
本题要求在删除某个点其余所有点都可以到达出口点,求最少的出口点的数量和方案数,不妨先按割点缩点,这样会形成一棵树,可以知道,度数为 1 的节点上必须要设置一个出口点,否则因为如果其父亲节点或根节点的唯一一个儿子节点被删除时该节点对应原图的所有节点就到达不了其他出口点,而对于那些度数大于 1 的节点来说无论删除哪一个节点来说其都可以达到其他的某个出口点,而对于那些,另外如果树中只有一个节点,可能本就是孤立点,显然要在其上面设置一个出口点,否则说明没有割点,其必须要设置两个出口点,因为如果被删除的点正好是出口点的话,其他点也可以到达另外一个出口点
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1005;
int n,m;
int h[N],ne[N],e[N],idx;
int timestamp,dfn[N],low[N],dcc_cnt,stk[N],top;
vector<int> dcc[N];
bool cut[N];
int root;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int x)
{
dfn[x]=low[x]=++timestamp;
stk[++top]=x;
if(h[x]==-1&&x==root)
{
dcc[++dcc_cnt].pb(x);
return ;
}
int cnt=0;
for(int i=h[x];~i;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[x]=min(low[x],low[j]);
if(dfn[x]<=low[j])
{
cnt++;
if(root!=x||cnt>1)cut[x]=true;
dcc_cnt++;
int y;
do
{
y=stk[top--];
dcc[dcc_cnt].pb(y);
}while(y!=j);
dcc[dcc_cnt].pb(x);
}
}
else
low[x]=min(low[x],dfn[j]);
}
}
int main()
{
int T=1;
while(cin>>m,m)
{
for(int i=1;i<=dcc_cnt;i++)dcc[i].clear();
timestamp=dcc_cnt=top=n=idx=0;
memset(h,-1,sizeof h);
memset(dfn,0,sizeof dfn);
memset(cut,0,sizeof cut);
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
n=max(n,x),n=max(n,y);
add(x,y),add(y,x);
}
for(root=1;root<=n;root++)
if(!dfn[root])tarjan(root);
int res1=0;
unsigned long long res2=1;
for(int i=1;i<=dcc_cnt;i++)
{
int cnt=0;
for(int j:dcc[i])cnt+=cut[j];
if(cnt==0)
{
if(dcc[i].size()>1)
res1+=2,res2*=dcc[i].size()*(dcc[i].size()-1)/2;
else
res1++;
}
else if(cnt==1)res1++,res2*=dcc[i].size()-1;
}
printf("Case %d: %d %llu\n",T++,res1,res2);
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!