Tarjan应用:无向图删点后剩余的连通分支数目

来源:互联网 发布:银天下软件下载 编辑:程序博客网 时间:2024/04/30 00:21
tarjan算法应用 ,问:无向图中删除哪个点后,剩下的连通分支数目最多,输出前k个使得剩下的连通分支最多的点的标号

和剩余的连通分支数目 。

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]);}} 


//之所以错写成这样,是对dfs理解不够,dfn[u]表示第一次访问到u节点的时间,
//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;}


0 0
原创粉丝点击