图论--无向图点双连通分量模板
2015-09-25 23:14
501 查看
对于一个无向图,如果一个点集,它内部的任意一个点对之间,至少有两条点完全不重复的路径,那么这个点集就是原图的一个点双连通分量,而点双联通分量之间是由割点隔开,割点就是如果删去这个点,原图的连通块数会增加,那么这个点就是割点。
通过tarjan算法,我们可以用一次 dfs 标记出所有的割点以及所有双连通分量。
注释版:
无注释版:
通过tarjan算法,我们可以用一次 dfs 标记出所有的割点以及所有双连通分量。
注释版:
#include<stdio.h> #include<string.h> #include<stack> #include<algorithm> #include<vector> using namespace std; const int maxn=1e5+5; const int maxm=1e5+5; int head[maxn],point[maxm],nxt[maxm],size; int n,t,bcccnt; //n点数,t是dfs的时间轴,bcccnt是双连通分量个数 int stx[maxn],low[maxn],bcc[maxn],cut[maxn]; //stx是节点在dfs时间轴的位置,low是该点能够通过后继节点到达的最远祖先,bcc是某个点所属的双连通分量编号(割点的编号无效),cut是是否为割点 vector<int>bccs[maxn]; //双连通分量内的节点 stack<int>S; void init(){ memset(head,-1,sizeof(head)); size=0; } void add(int a,int b){ point[size]=b; nxt[size]=head[a]; head[a]=size++; point[size]=a; nxt[size]=head[b]; head[b]=size++; } void dfs(int s,int pre){ stx[s]=low[s]=++t; //记录点的时间轴标号,初始化能访问到的最远祖先节点是自己 S.push(s); int son=0; //为了判定根节点是否是割点 for(int i=head[s];~i;i=nxt[i]){ int j=point[i]; if(!stx[j]){ son++; dfs(j,s); low[s]=min(low[s],low[j]); //用子节点的low值更新自己 if(low[j]>=stx[s]){ //如果子节点最远只能访问自己或后继节点,则出现双连通分量 cut[s]=1; //自己是割点 bcccnt++; bccs[bcccnt].clear(); while(1){ int u=S.top();S.pop(); bcc[u]=bcccnt; bccs[bcccnt].push_back(u); if(u==j)break; } bcc[s]=bcccnt; bccs[bcccnt].push_back(s); } } else if(j!=pre)low[s]=min(stx[j],low[s]); } if(pre==-1&&son==1)cut[s]=0; //若根节点只有一个子节点,则不是割点 } void setbcc(){ memset(cut,0,sizeof(cut)); memset(stx,0,sizeof(stx)); memset(bcc,0,sizeof(bcc)); t=bcccnt=0; for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1); } int main(){ int m; scanf("%d%d",&n,&m); init(); while(m--){ int a,b; scanf("%d%d",&a,&b); add(a,b); } setbcc(); for(int i=1;i<=n;++i){ printf("%d:%d %d\n",i,cut[i],bcc[i]); } for(int i=1;i<=bcccnt;++i){ printf("%d:",i); for(int j=0;j<bccs[i].size();++j){ printf("%d ",bccs[i][j]); } printf("\n"); } return 0; }
无注释版:
#include<stdio.h> #include<string.h> #include<stack> #include<algorithm> #include<vector> using namespace std; const int maxn=1e5+5; const int maxm=1e5+5; int head[maxn],point[maxm],nxt[maxm],size; int n,t,bcccnt; int stx[maxn],low[maxn],bcc[maxn],cut[maxn]; vector<int>bccs[maxn]; stack<int>S; void init(){ memset(head,-1,sizeof(head)); size=0; } void add(int a,int b){ point[size]=b; nxt[size]=head[a]; head[a]=size++; point[size]=a; nxt[size]=head[b]; head[b]=size++; } void dfs(int s,int pre){ stx[s]=low[s]=++t; S.push(s); int son=0; for(int i=head[s];~i;i=nxt[i]){ int j=point[i]; if(!stx[j]){ son++; dfs(j,s); low[s]=min(low[s],low[j]); if(low[j]>=stx[s]){ cut[s]=1; bcccnt++; bccs[bcccnt].clear(); while(1){ int u=S.top();S.pop(); bcc[u]=bcccnt; bccs[bcccnt].push_back(u); if(u==j)break; } bcc[s]=bcccnt; bccs[bcccnt].push_back(s); } } else if(j!=pre)low[s]=min(stx[j],low[s]); } if(pre==-1&&son==1)cut[s]=0; } void setbcc(){ memset(cut,0,sizeof(cut)); memset(stx,0,sizeof(stx)); memset(bcc,0,sizeof(bcc)); t=bcccnt=0; for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1); } int main(){ int m; scanf("%d%d",&n,&m); init(); while(m--){ int a,b; scanf("%d%d",&a,&b); add(a,b); } setbcc(); for(int i=1;i<=n;++i){ printf("%d:%d %d\n",i,cut[i],bcc[i]); } for(int i=1;i<=bcccnt;++i){ printf("%d:",i); for(int j=0;j<bccs[i].size();++j){ printf("%d ",bccs[i][j]); } printf("\n"); } return 0; }
相关文章推荐
- IOS TableView的Cell高度自适应,UILabel自动换行适应
- 算法导论习题,思考题题解博主录
- Scala深入浅出进阶经典 第52讲:Scala中路径依赖代码实战详解
- PHP+Sphinx+Mysql做搜索引擎
- 【转】git常用命令
- NetBeans中一个窗口的相对于另外一个窗口的位置
- 用dorado7建一个员工信息录
- 杭电oj-1166-敌兵布阵
- Scala深入浅出进阶经典 第51讲:Scala中链式调用风格的实现代码实战及其在Spark编程中的广泛运用
- DateTools,可能是最好用的iOS日期工具库
- scrapy抓取到中文,保存到json文件为unicode,如何解决.
- (原)用pixi.js 实现 方块阵点击后原地自转效果
- 首篇博客
- Scala深入浅出进阶经典 第50讲:Scala中Variance变化点及其在Spark中的应用源码解析
- iOS证书(Certificates)及配置文件(Provisioning Profile)说明:
- HDOJ1003 Max Sum(脑洞)
- 信息安全系统设计基础第三周学习总结
- 面试技巧
- activity启动模式之singleInstance
- Scala深入浅出进阶经典 第49讲:Scala中Variance代码实战及其在Spark中的应用源码解析