您的位置:首页 > 其它

CCCC训练练习题-矿工安全生产(求点割集)

2017-03-22 00:18 393 查看
题目链接:矿工安全生产

简单说一下就是给定一个无向联通图。要求找到割点(割点:若将该点和关联该点的边去掉,图的连通性发生改变),并把割点删去。求联通图的数量及在每个联通图上的某一个点放置一个救生器材的方案数。

我们先要找割点。

找割点的方法其实和找割边有异曲同工之妙。

从一点开始dfs该图可以得到一棵dfs树,设dfs开始的结点为根 rt 。

在dfs树中:

1.对于根结点,若它有两棵或两棵以上的子树,那么根结点一定是割点。

证明:假设根结点不是割点,那么当挖去根结点后,那么一定存在两棵子树之间是联通的,那么这样的话在dfs树上它们应该是一棵树,因此发生矛盾。

2.对于叶结点,一定不是割点。

证明:因为它的度数只有 1 ,因此删去后不改变连通性,所以一定不是割点。

3.对于非根非叶结点 u ,如果它的某棵子树中有一个点指向 u 的祖先,那么 u 一定不是割点,否则是割点。

证明:如果它的某棵子树中有一个点指向 u 的祖先,删除 u 后,u 的子树仍然与 u 的祖先有连通性,因此不是割点。否则一定是割点。

对于叶结点和根结点很好处理。对于非叶非根结点,我们可以采用在tarjan找强联通图算法中的 dfn[i] 和 low[i] 来判断是否有回边。dfn[i] 存储的是 i 点在dfs过程中被访问先后次序的序号, low[i] 存储的是 i 点及其子树联通的 dfn 最小的点。

若 low[i]>=dfn[i] ,则表示 i 及其子树不能访问到更早的结点,即没有回边,那么 i 就是割点。

当找到所有割点后,进行一次dfs找到连通块的数量即可。放置的方案数即每个联通块点数的乘积。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int dfn[maxn],low[maxn];
bool vis[maxn];
vector<int> adj[maxn];
bool ge[maxn];  //标记该点是否是割点
int id,rt;
void init(int _rt)
{
id=0;
rt=_rt;
for(int i=1;i<maxn;i++) adj[i].clear();
memset(vis,0,sizeof(vis));
memset(ge,0,sizeof(ge));
}
void tarjan(int u,int pre)
{
vis[u]=true;
dfn[u]=++id;
low[u]=dfn[u]; //初始化dfn和low
int cnt=0;
if(u!=rt&&adj[u].size()==1) return; //如果该点是叶子结点,不可能是割点,直接返回
for(int i=0;i<adj[u].size();i++)
{
int v=adj[u][i];
if(v==pre) continue;    //无向图
if(vis[v])
low[u]=min(low[u],dfn[v]);
else
{
cnt++;  //cnt记录dfs树中点u的子树数量
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
}
//low[u]>=dfn[u]就表示非根节点u的子树中没有点访问到u的祖先,即没有回边。
//若是根节点,只要它在dfs树中有两个或以上的子树就是割点。
if((low[u]>=dfn[u]&&u!=rt)||(u==rt&&cnt>=2))
ge[u]=true;
}
ll ans;
ll dfs(int u)
{
vis[u]=true;
ll res=1;
for(int i=0;i<adj[u].size();i++)
{
int v=adj[u][i];
if(!vis[v]&&!ge[v])
res+=dfs(v);
}
return res;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
if(!n) break;
int u,v;
int m=0;

init(1);
for(int i=0;i<n;i++)
{
scanf("%d%d",&u,&v);
m=max(m,max(u,v));
adj[u].push_back(v);
adj[v].push_back(u);
}
tarjan(1,0);
ans=1;

memset(vis,0,sizeof(vis));
int cnt=0;
for(int i=1;i<=m;i++)
if(!vis[i]&&!ge[i])
{
ans*=dfs(i);
cnt++;
}
printf("%d ",cnt);
cout << ans << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs