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找到连通块的数量即可。放置的方案数即每个联通块点数的乘积。
简单说一下就是给定一个无向联通图。要求找到割点(割点:若将该点和关联该点的边去掉,图的连通性发生改变),并把割点删去。求联通图的数量及在每个联通图上的某一个点放置一个救生器材的方案数。
我们先要找割点。
找割点的方法其实和找割边有异曲同工之妙。
从一点开始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; }
相关文章推荐
- 矿工安全生产
- 生产环境CentOS服务器系统安全配置
- 节后安全生产会议记要
- 如何保证自己的生产服务器安全
- 如何安全地配置一个生产环境的MongoDB服务器?
- 安全生产应急救援指挥系统之GIS一张图-flex/java
- CentOs生产环境中SSH安全配置
- Mysql生产环境安全规范
- 生产 tensorflow 训练使用的 .pkl 文件
- 如何保证自己的Linux生产服务器安全
- TensorFlow识别复杂验证码以及搭建生产环境(2)—— 搭建训练环境
- 软件安全测试实战训练
- 建设工程安全生产管理条例
- 在精益生产方式下深化生产作业安全
- 企业安全文化与建筑工程安全生产管理实践
- MySQL生产环境安全规范
- 安全观之我见(一):信息安全是一种生产要素 推荐
- 企业安全生产管理系统
- Linux Shell脚本生产环境下安全地删除文件
- 广州 《软件测试安全实战训练》圆满结束