割点,桥,双连通分量Tarjan ,入门练习
2016-05-02 20:09
519 查看
POJ 2117
求在一个无向图中,删去一个点,图中最多的连通块有多少个。
因为是无向图,初识的连通块block在,dfs里面就可以找到,然后我们只要找到割点,记录增加的连通块数目,最后扫一遍 取最大值就好了!
Wa的原因:初识化的ans 应该是初识连通块-1,我没读题,看样例做的,所以没怎么注意
Uva 796
题意:给一个无向图,按所连点的编号从小到大输出所有桥
Sample Input
8
0 (1) 1
1 (3) 2 0 3
2 (2) 1 3
3 (3) 1 2 4
4 (1) 3
7 (1) 6
6 (1) 7
5 (0)
0
Sample Output
3 critical links
0 - 1
3 - 4
6 - 7
0 critical links
这也是一个模板题吧,直接找出桥,然后从小到大输出即可
求在一个无向图中,删去一个点,图中最多的连通块有多少个。
因为是无向图,初识的连通块block在,dfs里面就可以找到,然后我们只要找到割点,记录增加的连通块数目,最后扫一遍 取最大值就好了!
Wa的原因:初识化的ans 应该是初识连通块-1,我没读题,看样例做的,所以没怎么注意
#include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <queue> #include <typeinfo> #include <fstream> #include <map> #include <stack> typedef long long ll; using namespace std; const int MAXN = 100010;//点数 const int MAXM = 1000010;//边数,因为是无向图,所以这个值要*2 struct Edge { int to,next; bool cut;//是否是桥标记 bool cong; }edge[MAXM]; int head[MAXN],tot; int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];// Belong数组表示属于哪一个连通块 int Index,top; bool Instack[MAXN]; bool cut[MAXN]; //自然用来存割点 int add_block[MAXN]; // 删除一个点后增加的连通块数 int bridge;//桥的数目 int block; // 连通块数量 void addedge(int u,int v) { edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false; head[u] = tot++; } void Tarjan(int u,int pre) { int v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; int son=0; // 统计某个点在搜索树中的儿子个数 for(int i = head[u];i != -1;i = edge[i].next) { v = edge[i].to; if(v == pre )continue; if( !DFN[v] ) { son++; Tarjan(v,u); if( Low[u] > Low[v] ) Low[u] = Low[v]; //一条边 (u,v)是桥,当且仅当(u,v)为树枝边(u,v都在栈里面),且满足DFN(u)<Low(v) if(Low[v] > DFN[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; //因为是无向图,自然增加了两条边 } //割点 //一个顶点u是割点,当且仅当满足(1)或者(2); (1)u为树根,且u有多于一个子树。 //(2)u不为树根,且满足存在(u,v)为树枝边(u为v在搜索树中的父亲),使DFS(u)<=Low(v) if(u!=pre && Low[v] >= DFN[u]){ cut[u] =true; add_block[u]++; } } else if( Instack[v] && Low[u] > DFN[v] ) Low[u] = DFN[v]; } //数根,分支数>1 if(u== pre && son>1 ) cut[u]=true; if(u== pre) add_block[u]=son-1; Instack[u]=false; top--; } void init() { tot = 0; memset(head,-1,sizeof(head)); } void solve(int N){ memset(DFN,0,sizeof(DFN)); memset(Instack,false,sizeof(Instack)); memset(add_block,0,sizeof(add_block) ); memset(cut,false,sizeof(cut) ); Index=top=block=0; for(int i=1;i<=N;i++) if(!DFN[i]){ block++; Tarjan(i,i); } } int main(){ int p,c; //freopen("1.txt","r",stdin); while(~scanf("%d %d",&p,&c) &&( p||c)){ init(); for(int i=0;i<c;i++){ int u,v; scanf("%d %d",&u,&v); u++,v++; addedge(u,v); addedge(v,u); } solve(p); int ans=block-1; for(int i=1;i<=p;i++){ // printf("add=%d ",add_block[i]); ans=max(ans,block+add_block[i]); } printf("%d\n",ans); } return 0; }
Uva 796
题意:给一个无向图,按所连点的编号从小到大输出所有桥
Sample Input
8
0 (1) 1
1 (3) 2 0 3
2 (2) 1 3
3 (3) 1 2 4
4 (1) 3
7 (1) 6
6 (1) 7
5 (0)
0
Sample Output
3 critical links
0 - 1
3 - 4
6 - 7
0 critical links
这也是一个模板题吧,直接找出桥,然后从小到大输出即可
/* 求无向图的割点和桥 可以找出 割点和桥,求出删掉每个点后增加的连通块 需要注意重边的处理,可以先用矩阵存,再转领接表,或者判重 */ #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <queue> #include <typeinfo> #include <fstream> #include <map> #include <stack> typedef long long ll; using namespace std; const int MAXN = 100010;//点数 const int MAXM = 1000010;//边数,因为是无向图,所以这个值要*2 struct Edge { int to,next; bool cut;//是否是桥标记 bool cong; }edge[MAXM]; int head[MAXN],tot; int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];// Belong数组表示属于哪一个连通块 int Index,top; bool Instack[MAXN]; bool cut[MAXN]; //自然用来存割点 int add_block[MAXN]; // 删除一个点后增加的连通块数 int bridge;//桥的数目 int block; // 连通块数量 int uu[MAXN]; void addedge(int u,int v) { edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false;uu[tot]=u; head[u] = tot++; } void Tarjan(int u,int pre) { int v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; int son=0; // 统计某个点在搜索树中的儿子个数 for(int i = head[u];i != -1;i = edge[i].next) { v = edge[i].to; if(v == pre )continue; if( !DFN[v] ) { son++; Tarjan(v,u); if( Low[u] > Low[v] ) Low[u] = Low[v]; //一条边 (u,v)是桥,当且仅当(u,v)为树枝边(u,v都在栈里面),且满足DFN(u)<Low(v) if(Low[v] > DFN[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; //因为是无向图,自然增加了两条边 } //割点 //一个顶点u是割点,当且仅当满足(1)或者(2); (1)u为树根,且u有多于一个子树。 //(2)u不为树根,且满足存在(u,v)为树枝边(u为v在搜索树中的父亲),使DFS(u)<=Low(v) if(u!=pre && Low[v] >= DFN[u]){ cut[u] =true; add_block[u]++; } } else if( Instack[v] && Low[u] > DFN[v] ) Low[u] = DFN[v]; } //数根,分支数>1 if(u== pre && son>1 ) cut[u]=true; if(u== pre) add_block[u]=son-1; Instack[u]=false; top--; } void init() { tot = 0; memset(head,-1,sizeof(head)); } void solve(int N){ memset(DFN,0,sizeof(DFN)); memset(Instack,false,sizeof(Instack)); memset(add_block,0,sizeof(add_block) ); memset(cut,false,sizeof(cut) ); Index=top=block=bridge=0; for(int i=1;i<=N;i++) if(!DFN[i]){ block++; Tarjan(i,i); } } struct ans{ int u,v; }; bool cmp(ans a,ans b){ if(a.u==b.u) return a.v<b.v; else return a.u<b.u; } int main(){ int n; //freopen("1.txt","r",stdin); while(~scanf("%d",&n)){ int m=0; init(); // 要判断重边,因为我们的有向边和无向边是要建在一起的。 for(int i=0;i<n;i++){ int u,v_num; scanf("%d (%d)",&u,&v_num); u++; for(int k=0;k<v_num;k++){ int v; scanf("%d",&v); v++; if(u>=v) continue; //处理重边 addedge(u,v); addedge(v,u); // printf("add=%d %d\n",u,v); m+=2; } } solve(n); printf("%d critical links\n",bridge); for(int i=0;i<m;i+=2){ if(edge[i].cut==1){ printf("%d - %d\n",uu[i]-1,edge[i].to-1); } } printf("\n"); } return 0; }
相关文章推荐
- 针对hibernate应用的表设计,最好设计一个非逻辑主键id吗?大家怎么做的
- Android:studio 导入工程报错finished with non-zero exit value 1解决方案
- 从假洋河酒事件看 电商监督制度缺失
- online_judge_1208
- 解决UDP服务器并发困难
- 欧几里得算法求最大公约数的递归和非递归实现
- java的静态代理
- List--LinkedList(浅识)二
- Volley相关知识
- Python爬虫折腾纪录
- 待整理内容
- nyoj_42 一笔画问题
- 3. Longest Substring Without Repeating Characters
- 动态规划——最少硬币问题
- hdu 5677 ztr loves substring 多重背包
- Android Studio 文件名颜色代表含义
- elk搭建完整搭建【篇1】
- Golang实现红黑树
- iOS中黄色文件夹和蓝色文件夹的区别
- 文章标题