Poj 1144 Network (割点)
2013-09-08 19:52
337 查看
题意:求无向图的割点的个数。
输入数据有多组。每组数据的第一行N,代表顶点个数(编号1到N)。以下最多N行,最后一行是一个0。每行第一个数u,以后X(不确定X是多少)个数v,表示边(u,v)。输入以N=0结束。N < 100
思路:Tarjan算法求割点。
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn(u)<=low(v)。
对条件2的说明:u不为树根,那么u肯定有祖先,如果存在dfn(u)<=low(v),表示u的子孙只能通过u才能访问u的祖先,这也就是说,不通过u,u的子孙无法访问u的祖先,那么如果去掉u这个节点,就会至少分为两个子图,一个是u祖先,一个是u子孙的。
这篇博文 http://blog.csdn.net/zhang20072844/article/details/8105013
的评论里有一个例子。
2014-7-29 更新 贴个套用常用的模板写的
输入数据有多组。每组数据的第一行N,代表顶点个数(编号1到N)。以下最多N行,最后一行是一个0。每行第一个数u,以后X(不确定X是多少)个数v,表示边(u,v)。输入以N=0结束。N < 100
思路:Tarjan算法求割点。
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn(u)<=low(v)。
对条件2的说明:u不为树根,那么u肯定有祖先,如果存在dfn(u)<=low(v),表示u的子孙只能通过u才能访问u的祖先,这也就是说,不通过u,u的子孙无法访问u的祖先,那么如果去掉u这个节点,就会至少分为两个子图,一个是u祖先,一个是u子孙的。
这篇博文 http://blog.csdn.net/zhang20072844/article/details/8105013
的评论里有一个例子。
#include <cstdio> #include <cstring> #define min(x,y) ((x)<(y)?(x):(y)) const int nPoint=105; const int nEdges=3005; class Qiangliantong { private: struct Edge { int v,next; }edges[nEdges]; int dfn[nPoint],low[nPoint],head[nPoint],father[nPoint]; bool cut[nPoint]; int n,e,id; void Dfs (int u,int p) { int i,v; dfn[u]=low[u]=++id; father[u]=p; for (i=head[u];i!=-1;i=edges[i].next) { v=edges[i].v; if (!dfn[v]) { Dfs(v,u); low[u]=min(low[u],low[v]); } else low[u]=min(low[u],dfn[v]); } } public: void Init (int _n) { n=_n; e=0; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(father,0,sizeof(father)); memset(cut,false,sizeof(cut)); //cut存储是否为割点 } void Add (int u,int v) { edges[e].v=v; edges[e].next=head[u]; head[u]=e++; } int CalCutNum () { int i,ans=0,num=0; //num存储根节点有多少子节点 //图是连通的,顶点编号1到n Dfs(1,-1); for (int v=2;v<=n;v++) //遍历图中除根节点之外的所有节点 { int u=father[v]; if (u==1) num++; //如果父节点为根节点 else if (dfn[u]<=low[v]) cut[u]=true; } if (num>1) //对于根节点,如果有不止一个孩子,则根节点也是割点 cut[1]=true; for (i=1;i<=n;i++) if (cut[i]) ans++; return ans; } }ob; int main () { int n,u,v; while (scanf("%d",&n),n) { ob.Init (n); while (scanf("%d",&u),u) while (getchar()!='\n') { scanf("%d",&v); ob.Add(u,v); ob.Add(v,u); } printf("%d\n",ob.CalCutNum()); } return 0; }
2014-7-29 更新 贴个套用常用的模板写的
#include <cstdio> #include <cstring> #include <stack> #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) using namespace std; const int INF=0x3f3f3f3f; const int nPoint=1010; //原节点数 const int nEdges=30005; //原边数 const int nNewmap=1005; //新图最大节点数 class SCC // strongly connected components {//节点标号从1开始 private: struct Edge { int v,next; }edges[nEdges]; int dfn[nPoint],low[nPoint],head[nPoint];//dfn(u)为节点u搜索的次序编号(时间戳),low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号 bool visit[nPoint]; //标记是否在栈中 int in[nPoint],out[nPoint],color[nPoint]; //color[]保存各强连通分量包含的节点数,in[]各强连通分量的入度,out[]各强连通分量的出度 int belong[nPoint]; //每个结点所对应的强连通分量标号数组 bool DAG[nNewmap][nNewmap]; //存储缩点之后的新图,有向无环图DAG int n,e,id,colornum; //colornum强连通分量的个数 int nIn_0,nOut_0; //nIn_0=0入度为0的点的个数,nOut_0=0出度为0的点个数 stack<int> S; int cnt[nPoint]; //存储割点被切掉后会分出的块数,没有减去原来那块,可用来统计割点个数(不为0即为割点) void DFS (int u) { int i,top,v; dfn[u]=low[u]=++id; //id为时间戳 S.push(u); visit[u]=true; for (i=head[u];i!=-1;i=edges[i].next) { v=edges[i].v; if (dfn[v]==0) {//未被访问 DFS(v);//继续向下找 if (low[v]>=dfn[u]) //是割点,统计 cnt[u]++; low[u]=min(low[u],low[v]);//更新u节点所能到达的最小层数 } else if (visit[v]) low[u]=min(low[u],dfn[v]); } if (dfn[u]<=low[u]) // == {//如果节点u是强连通分量的根 colornum++; //连通分量标号+1 do { top=S.top(); // S.pop(); visit[top]=false; belong[top]=colornum; //出栈节点top属于colornum标号的强连通分量 color[colornum]++; }while (top!=u); //直接将u从栈中退出 } } public: void Init (int _n) { n=_n; id=colornum=e=0; nIn_0=nOut_0=0; memset(head,-1,sizeof(head)); memset(visit,false,sizeof(visit)); memset(dfn,0,sizeof(dfn)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(cnt,0,sizeof(cnt)); } void Add (int u,int v) { edges[e].v=v; edges[e].next=head[u]; head[u]=e++; } int Tarjan () //返回强连通分量的个数 { int i,num=0; while (!S.empty()) //清空栈 S.pop(); for (i=1;i<=n;i++) //枚举每个节点,搜索连通分量 if (!dfn[i]) //未被访问 { num++; DFS(i); //则找该节点的连通分量,也可以在这里计算colornum cnt[i]--; //原来本身有一块 } return num; } void Cal () //计算in[],out[],nOut_0,nIn_0 {//与TopoOrder ()同时调则无法正确计算 in 数组(重复使用) int i,j; Tarjan(); for (i=1;i<=n;i++) for (j=head[i];j!=-1;j=edges[j].next) if (belong[i]!=belong[edges[j].v]) { in[belong[edges[j].v]]++; out[belong[i]]++; } for (i=1;i<=colornum;i++) { nOut_0+=!out[i]; nIn_0+=!in[i]; } } void Build () //建立新图DAG { memset(DAG,0,sizeof(DAG)); for (int i=1;i<=n;i++) for (int j=head[i];j!=-1;j=edges[j].next) if (belong[i]!=belong[edges[j].v]) DAG[belong[i]] [belong[edges[j].v]] =true; } bool TopoOrder () //拓扑排序,返回是否有分叉 {//既是否为一条链 int i,j; for (i=1;i<=colornum;i++) for (j=1;j<=colornum;j++) if (DAG[i][j]) in[j]++; for (i=1;i<colornum;i++) { int cnt=0; //分支条数 int p=0; //下一节点 for (j=1;j<=colornum && cnt<=1;j++) if (in[j]==0) cnt++,p=j; if (cnt>1) return false; for (j=1;j<=colornum;j++) if (DAG[p][j]) in[j]--; in[p]=INF; } return true; } void Deal () {//计算割点数目 Tarjan (); int k=0; for (int i=1;i<=n;i++) if (cnt[i]) k++; printf("%d\n",k); } }ob; int main () { int n,u,v; while (scanf("%d",&n),n) { ob.Init (n); while (scanf("%d",&u),u) while (getchar()!='\n') { scanf("%d",&v); ob.Add(u,v); ob.Add(v,u); } ob.Deal(); } return 0; }
相关文章推荐
- POJ 1144 Network 笔记
- poj 1144 Network 无向图求割点
- POJ 1144 Network(Tarjan)
- Poj 1144 Network
- 【poj1144】 Network
- POJ 1144 Network(简单求无向图割顶数)
- poj 1144 Network 图的割顶判断模板
- POJ 1144 Network
- POJ 1144 Network(无向图连通分量求割点)
- POJ 1144 Network
- poj 1144 Network(模板题)(Tarjan 关节点的朴素算法)
- poj 1144 Network【双连通分量求割点总数】
- poj 1144 Network 【求无向图中割点总数】【点双联通入门题目】
- POJ 1144 Network(割点)
- POJ 1144 Network(无向图连通分量求割点)
- poj 1144 Network (tarjan割点模板)
- POJ 1144 Network 裸割点
- POJ 1144 Network 【求割点总数】
- POJ 1144 Network 求割点(tarjan)
- Network_poj1144_割点