题目描述 Description
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入描述 Input Description
输入文件有若干组数据,每组数据的第一行是一个正整数N(N≤500),表示工地的隧道数,接下来的N 行每行是用空格隔开的两个整数S 和T,表示挖煤点S 与挖煤点T 由隧道直接连接。输入数据以0 结尾。
输出描述 Output Description
输入文件中有多少组数据,输出文件中就有多少行。每行对应一组输入数据的结果。其中第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
数据范围及提示 Data Size & Hint
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
分类标签 Tags 点此展开
题意:
*给定一个n个点,m条边的图。要在这n个点选择若干点建造避难所,使得无论哪个点爆炸,每个点上的人都能逃到这个避难所去。
*求最少避难所个数,以及在这个前提下的方案总数。
解析:
*求出所有割点后。对于剩下的所有联通块都缩成一个点。
*接下来对于所有点,若该点与两个点相连,则不需要建立避难所,若只与一个点相连,则需要建立1个避难所,若没有相连,则需要建立2个避难所。
*方案总数用乘法原理乘起来就可以了。
AC代码:
#include<cstdio> #include<cstring> #include<vector> #define ll long long using namespace std; inline const int read(){ register int x=0,f=1; register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } const int N=510; vector<int>grap[N]; int n,m,cas,pd,dfn[N],low[N],son,ans,sum; int siz[N],r[N],matc[N]; bool f[N],cut[N],mark[N]; ll cnt; void Cl(){ cnt=1;pd=0;sum=0;n=0;son=0;ans=0; memset(f,0,sizeof f); memset(r,0,sizeof r); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(cut,0,sizeof cut); memset(siz,0,sizeof siz); memset(mark,0,sizeof mark); memset(grap,0,sizeof grap); memset(matc,0,sizeof matc); } void tarjan(int v,int root){ dfn[v]=low[v]=++pd; mark[v]=1; for(int i=0;i<grap[v].size();i++){ int w=grap[v][i]; if(!dfn[w]){ tarjan(w,root); low[v]=min(low[v],low[w]); if(low[w]>=dfn[v]&&v!=root) cut[v]=1; else if(v==root) son++; } else if(mark[w]){ low[v]=min(low[v],dfn[w]); } } } void dfs(int x){ f[x]=1;siz[sum]++; for(int i=0;i<grap[x].size();i++){ int v=grap[x][i]; if(f[v]) continue; if(!cut[v]) dfs(v); else if(matc[v]!=sum){ matc[v]=sum;r[sum]++; } } } int main(){ for(m=read();m;m=read()){ Cl(); for(int x,y,i=1;i<=m;i++){ scanf("%d%d",&x,&y); n=max(n,x); n=max(n,y); grap[x].push_back(y); grap[y].push_back(x); } for(int i=1;i<=n;i++) if(!dfn[i]){ son=0; tarjan(i,i); if(son>1) cut[i]=1; } for(int i=1;i<=n;i++) if(!cut[i]&&!f[i]){ sum++;dfs(i); } if(sum==1){ ans=2; cnt=(ll)n*(n-1)/2; } else{ for(int i=1;i<=sum;i++){ if(r[i]==1){ ans++; cnt*=(ll)siz[i]; } } } printf("Case %d: %d %lld ",++cas,ans,cnt); } return 0; }