Tarjan应用:无向图删点后剩余的连通分支数目
来源:互联网 发布:银天下软件下载 编辑:程序博客网 时间:2024/04/30 00:21
和剩余的连通分支数目 。
input
多组数据,EOF结束。
第1行:N和M和K 第2到第M+1行:每一行2个数Ui和Vi,表示Ui到Vi之间有一条边。
给定一个无向图(且可能不连通),删去一个点和与该点相连的所有边
图的连通性可能发生变化
本题考查连通分量的数目
如果删除了一个孤立的点,那么总的连通分量会减少
如果删除了一个非根割顶,那么总的连通分量会增加
增加多少个呢?
设置一个变量block[u],表示删除了u节点后,u原来所在的分支,将会分裂成多少棵分支 。
统计出原来的图中有多少个连通分支b,则删除一个点u之后,总的分支数目将变成b-1+block[u]。
试想,一个非根割顶被删,它的祖先会形成一个连通分量
同时它的每棵子树形成一个连通分量。
但是子树可能会和祖先连通,这种情况下,删除本割顶并没有将这样的子树独立出来,
这意味着block[u]对这样的子树不会增加
初始化block[u]:如果u是某根节点,block[u]=0(它已经没有长辈了),否则block[u]=1。
Tarjan中的tips:
void dfs(int u,int pa){dfn[u]=low[u]=++t;for(int i=0;i<edges[u].size();i++){int v=edges[u][i];if(v==pa)continue;if(!low[v]){dfs(v,u);if(low[v]>=dfn[u])block[u]++; else low[u]=min(low[u],low[v]);}else low[u]=min(low[u],dfn[v]);}}
下面是wa的代码
void dfs(int u,int pa){dfn[u]=low[u]=++t;for(int i=0;i<edges[u].size();i++){int v=edges[u][i];if(v==pa)continue;if(!low[v]){dfs(v,u);if(low[v]>=low[u])block[u]++;}low[u]=min(low[u],low[v]);}}
//low[u]是u节点能够追溯到的最早的时间,正确代码中的两处dfn[u]处均不能改成low[u]
//原因是
//low[u]可能已经被u的后向边更改
// 考虑下面的图,0到1,1到2,为方便起见,设初始时刻为0.
//2先去访问0,进行更新low[2]=0,2再去访问3,
//3最早能追溯到1,low[3]=1,根据wa的代码,这时候要比较if(low[v]>=low[u])block[u]++;
//我们惊讶地发现,block[2]成为了2,这意味着,把2删除后,会产生两个强连通分支。显然是错误的。
再考虑第二个位置被写错会发生什么事情:
假设我已经把第一个错误改正,我们得到下面的代码,它仍然是错误的:
void dfs(int u,int pa){dfn[u]=low[u]=++t;for(int i=0;i<edges[u].size();i++){int v=edges[u][i];if(v==pa)continue;if(!low[v]){dfs(v,u);if(low[v]>=dfn[u])block[u]++; else low[u]=min(low[u],low[v]);}else low[u]=min(low[u],low[v]);}}
我们将使用下图:
开始
0到1,1到2, 2访问0, low[1]=0。
1到3, 3到4 ,4访问1,现在要执行low[u]=min(low[u],low[v]);
即low[4]=0。这样就更改了原图的拓扑关系。
工作继续,接下来会发生什么事情呢?
low[3]=0
然后,镜头回到1节点,关键的一步
判断if(low[v]>=dfn[u])block[u]++;
else low[u]=min(low[u],low[v]);
这时的low[3]=0,dfn[1]=1,block[1]不会增长,并且因为4节点已经访问过了,block[1]以后也不会有增长的机会了,
这意味着删除1节点,将会只得到1个连通分支,这同样是错误的。
代码
#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;int N,M,k;#define maxn 10000vector<int> edges[maxn+10];int block[maxn+10];int dfn[maxn+10],low[maxn+10],t;void dfs(int u,int pa){dfn[u]=low[u]=++t;for(int i=0;i<edges[u].size();i++){int v=edges[u][i];if(v==pa)continue;if(!low[v]){dfs(v,u);if(low[v]>=dfn[u])block[u]++; else low[u]=min(low[u],low[v]);}else low[u]=min(low[u],dfn[v]);}}int ran[maxn+10];void init(){for(int i=0;i<N;i++)edges[i].clear();for(int i=0;i<N;i++)block[i]=1;for(int i=0;i<N;i++)ran[i]=i;}int cmp(int a,int b){if(block[a]>block[b])return 1;else if(block[a]==block[b])return a<b;return 0;}int b;int main(){while(scanf("%d%d%d",&N,&M,&k)==3){init();for(int i=0,u,v;i<M;i++){scanf("%d%d",&u,&v);edges[u].push_back(v);edges[v].push_back(u);}t=b=0;memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));for(int i=0;i<N;i++){if(!low[i]){block[i]=0;b++;dfs(i,-1);}}sort(ran,ran+N,cmp);for(int i=0;i<k;i++){printf("%d %d\n",ran[i],b-1+block[ran[i]]);}cout<<endl;}return 0;}
- Tarjan应用:无向图删点后剩余的连通分支数目
- 无向连通图求割点(tarjan算法去掉改割点剩下的联通分量数目)
- 无向图的连通分支
- 无向图的连通分支
- 有向图强连通分支的Tarjan算法
- POJ1523.SPF——无向图的割点,并求连通分支数(tarjan算法)
- 无向图连通分支
- 计算无向图中连通块的数目
- 无向图 点连通tarjan算法 求割点 + 求BCC以及BCC里面的点 + 求去掉每个点后图中BCC数目 【总结】
- 无向图 点连通tarjan算法 求割点 + 求BCC以及BCC里面的点 + 求去掉每个点后图中BCC数目 【总结】
- 15、无向图的各连通分支
- 算法基础 - 求有向图的强连通分支(Tarjan算法)
- poj 2117(tarjan 求无向图去掉一点的连通分量)
- poj1523 SPF 无向连通图 求割点 关节点 tarjan算法
- zoj Burning Bridges 无向连通图 求割点 桥 tarjan
- poj3710 Christmas Game 无向图 tarjan 连通分量
- 用Tarjan算法求无向连通图割点&&割边
- 有向连通分量的Tarjan算法
- Firefox鼠标手势--FireGestures
- C# 控制台应用程序--随机数
- Design Pattern Template 模板设计模式
- 给IT新人的15点建议:苦逼程序员的辛酸反省与总结
- 进程上下文和中断上下文
- Tarjan应用:无向图删点后剩余的连通分支数目
- gdb 调试
- 在VS2010平台上创建并使用dll
- 导出Excel2 - 项目分解篇
- 而坠轮回
- linux 中断机制的处理过程
- python使用第三方函数库及简单爬虫实验
- SurfaceView概述
- 你真的了解C#中的值和引用?