强连通相关:poj1236,poj2186,poj2762,hdu4738
2015-09-04 20:25
411 查看
相关的概念:
割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
对于有向图上的2个点a,b,若存在一条从a到b的路径,也存在一条从b到a的路径,那么称a,b是强连通的。
对于有向图上的一个子图,若子图内任意点对(a,b)都满足强连通,则称该子图为强连通子图。
非强连通图有向图的极大强连通子图,称为强连通分量。
对于一个无向图的子图,当删除其中任意一条边后,不改变图内点的连通性,这样的子图叫做边的双连通子图。而当子图的边数达到最大时,叫做边的双连通分量。
将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。
tarjan基本模板
相关题目poj1236
题意:给出点的数量,给出与之相连的单向边。至少选几个点,能从这些点到达所有点。至少加多少条边,能从任何一个点到达所有点。
思路:求强连通分量,缩点,求出新图的入度为0的点个数n,出度为0的点个数m。
问题1:ans=n; 问题2:ans=max(n,m);
poj2186
题意:有n只牛,牛A认为牛B很牛,牛B认为牛C很牛。给你M个关系(谁认为谁牛),如果牛A认为牛B很牛,牛B认为牛C很牛。那么我们就认为牛A认为牛C很牛。求大家都认为它很牛的牛有几只。
思路:求强连通分量,缩点,出度为0的点只能有一个,答案就是该连通分量中的点的数量。
poj2762
题意:给出一个有向图,求出该图是否能满足给出两个点,能从一个a到b或者从b到a。
思路:一个强连通分量中的点肯定满足,所以先缩点,在该弱连通图中判断改图是否是单链的(toposort),若有分叉则分叉上的两个点是不能互达的。
hdu4738
题意: 曹操有N个岛,这些岛用M座桥连接起来,每座桥有士兵把守(也可能没有),周瑜想让这N个岛不连通,但只能炸掉一座桥,并且炸掉一座桥需要派出不小于守桥士兵数的人。
思路:首先判断图是否连通,不连通则不需要去炸桥,输出0,图连通,则可以用Tarjan找割边,割边不存在输出-1表示不能达到目的,找到所有的割边,只需要炸掉其中守兵数最少的桥即可。
PS: 桥的守兵数为0时,也需要派出一个人去炸桥!
割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
对于有向图上的2个点a,b,若存在一条从a到b的路径,也存在一条从b到a的路径,那么称a,b是强连通的。
对于有向图上的一个子图,若子图内任意点对(a,b)都满足强连通,则称该子图为强连通子图。
非强连通图有向图的极大强连通子图,称为强连通分量。
对于一个无向图的子图,当删除其中任意一条边后,不改变图内点的连通性,这样的子图叫做边的双连通子图。而当子图的边数达到最大时,叫做边的双连通分量。
将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。
tarjan基本模板
#include<bits/stdc++.h> using namespace std; #define N 30000 int dfn ;//深搜的次序 int low ;//能追溯到的最早的次序 int head ;//邻接表 int belong ;//属于哪个强连通分量 int f ;//父节点 bool instack ; int k,cnt,num;//k是邻接表中边的数量,cnt强连通分量个数,num深搜的次序。 struct edge { int u,v,next; } e[N*5]; void add(int u,int v) { e[k].v=v; e[k].next=head[u]; head[u]=k++; } stack<int>s; void dfs(int u,int id)//有向图不需要参数id { dfn[u]=low[u]=++num; instack[u]=true; s.push(u); for(int i=head[u]; i!=-1; i=e[i].next) { if(i==(1^id)) continue;//无向图需要这个条件 int v=e[i].v; if(!dfn[v]) { f[v]=u; dfs(v,i); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; int v; do { v=s.top(); s.pop(); instack[v]=false; belong[v]=cnt; } while(u!=v); } } int main() { int n,m,x,y; cin>>n>>m; memset(head,-1,sizeof(head)); while(m--) { cin>>x>>y; add(x,y); add(y,x); } for(int i=1; i<=n; i++) { if(!dfn[i]) { dfs(i);//有向图 dfs(i,-1);//无向图 } } }
相关题目poj1236
题意:给出点的数量,给出与之相连的单向边。至少选几个点,能从这些点到达所有点。至少加多少条边,能从任何一个点到达所有点。
思路:求强连通分量,缩点,求出新图的入度为0的点个数n,出度为0的点个数m。
问题1:ans=n; 问题2:ans=max(n,m);
#include<iostream> #include<cstring> #include<stack> using namespace std; #define N 155 int n,k,num,cnt; int head ,dfn ,in ,out ,belong ,low ; bool instack ; struct edge { int u,v,next; } e[N*N]; void add(int u,int v) { e[k].u=u; e[k].v=v; e[k].next=head[u]; head[u]=k++; } stack<int>s; void dfs(int u) { dfn[u]=low[u]=++num; instack[u]=true; s.push(u); for(int i=head[u]; i!=-1; i=e[i].next) { int v=e[i].v; if(!dfn[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; int v; do { v=s.top(); s.pop(); instack[v]=false; belong[v]=cnt; } while(u!=v); } } int main() { memset(head,-1,sizeof(head)); cin>>n; for(int i=1; i<=n; i++) { int x; while(cin>>x,x) { add(i,x); } } for(int i=1; i<=n; i++) { if(!dfn[i]) dfs(i); } for(int i=0; i<k; i++)//求出缩点后的新图中的入度和出度 { int x=belong[e[i].u],y=belong[e[i].v]; if(x!=y) in[y]++,out[x]++; } int x=0,y=0; for(int i=1; i<=cnt; i++) { if(!in[i]) x++; if(!out[i]) y++; } if(cnt==1)//特例,强连通分量只有一个,不需要加边 { cout<<1<<endl; cout<<0<<endl; } else { cout<<x<<endl; cout<<max(x,y)<<endl; } }
poj2186
题意:有n只牛,牛A认为牛B很牛,牛B认为牛C很牛。给你M个关系(谁认为谁牛),如果牛A认为牛B很牛,牛B认为牛C很牛。那么我们就认为牛A认为牛C很牛。求大家都认为它很牛的牛有几只。
思路:求强连通分量,缩点,出度为0的点只能有一个,答案就是该连通分量中的点的数量。
#include<cstring> #include<iostream> #include<stack> using namespace std; #define N 30000 int dfn ,low ,head ,belong ,out ; bool instack ; int k,cnt,num; struct edge { int u,v,next; } e[N*5]; void add(int u,int v) { e[k].u=u; e[k].v=v; e[k].next=head[u]; head[u]=k++; } stack<int>s; void dfs(int u) { dfn[u]=low[u]=++num; instack[u]=true; s.push(u); for(int i=head[u]; i!=-1; i=e[i].next) { //if(i==(1^id)) continue; int v=e[i].v; if(!dfn[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; int v; do { v=s.top(); s.pop(); instack[v]=false; belong[v]=cnt; } while(u!=v); } } int main() { int n,m,x,y; cin>>n>>m; memset(head,-1,sizeof(head)); while(m--) { cin>>x>>y; add(x,y); } for(int i=1; i<=n; i++) { if(!dfn[i]) { dfs(i); } } int c=0; for(int i=0; i<k; i++) { if(belong[e[i].u]!=belong[e[i].v]) out[belong[e[i].u]]++; } for(int i=1; i<=cnt; i++) if(!out[i]) c++; if(c!=1) cout<<0<<endl; else { int ans=0; for(int i=1; i<=n; i++) if(out[belong[i]]==0) ans++; cout<<ans<<endl; } }
poj2762
题意:给出一个有向图,求出该图是否能满足给出两个点,能从一个a到b或者从b到a。
思路:一个强连通分量中的点肯定满足,所以先缩点,在该弱连通图中判断改图是否是单链的(toposort),若有分叉则分叉上的两个点是不能互达的。
#include<cstring> #include<iostream> #include<stack> #include<queue> using namespace std; #define N 3000 int dfn ,low ,head ,belong ,in ,out ; bool instack ; int k,cnt,num; struct edge { int u,v,next; } e[N*5]; void add(int u,int v) { e[k].u=u; e[k].v=v; e[k].next=head[u]; head[u]=k++; } stack<int>s; void dfs(int u) { dfn[u]=low[u]=++num; instack[u]=true; s.push(u); for(int i=head[u]; i!=-1; i=e[i].next) { //if(i==(1^id)) continue; int v=e[i].v; if(!dfn[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; int v; do { v=s.top(); s.pop(); instack[v]=false; belong[v]=cnt; } while(u!=v); } } vector<int>E[N*5];//新图的邻接表 queue<int>q; bool toposort() { while(!q.empty()) q.pop(); for(int i=1; i<=cnt; i++) if(!in[i]) q.push(i); if(q.size()>1) return false;//入点有多个不满足 while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<E[u].size(); i++) { int v=E[u][i]; if(!--in[v]) q.push(v); } if(q.size()>1) return false;//有分叉不满足 } return true; } int main() { int T; cin>>T; while(T--) { int n,m,x,y; cin>>n>>m; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); num=cnt=k=0; while(m--) { cin>>x>>y; add(x,y); } for(int i=1; i<=n; i++) { if(!dfn[i]) { dfs(i); } } memset(in,0,sizeof(in)); for(int i=1; i<=cnt; i++) E[i].clear(); for(int i=0; i<k; i++) { int x=belong[e[i].u]; int y=belong[e[i].v]; if(x!=y) { in[y]++; E[x].push_back(y); } } if(toposort()) cout<<"Yes"<<endl; else cout<<"No"<<endl; } }
hdu4738
题意: 曹操有N个岛,这些岛用M座桥连接起来,每座桥有士兵把守(也可能没有),周瑜想让这N个岛不连通,但只能炸掉一座桥,并且炸掉一座桥需要派出不小于守桥士兵数的人。
思路:首先判断图是否连通,不连通则不需要去炸桥,输出0,图连通,则可以用Tarjan找割边,割边不存在输出-1表示不能达到目的,找到所有的割边,只需要炸掉其中守兵数最少的桥即可。
PS: 桥的守兵数为0时,也需要派出一个人去炸桥!
#include<bits/stdc++.h> using namespace std; #define N 2000 #define inf 0x7ffffff int dfn ,low ,head ,belong ,f ; bool instack ; int k,cnt,num; struct edge { int u,v,w,next; } e[N*N]; void add(int u,int v,int w) { e[k].u=u; e[k].v=v; e[k].w=w; e[k].next=head[u]; head[u]=k++; } stack<int>s; void dfs(int u,int id) { dfn[u]=low[u]=++num; instack[u]=true; s.push(u); for(int i=head[u]; i!=-1; i=e[i].next) { if(i==(1^id)) continue; int v=e[i].v; if(!dfn[v]) { f[v]=u; dfs(v,i); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; int v; do { v=s.top(); s.pop(); instack[v]=false; belong[v]=cnt; } while(u!=v); } } int main() { int n,m,x,y,w; while(cin>>n>>m,n+m) { memset(head,-1,sizeof(head)); memset(instack,false,sizeof(instack)); memset(f,-1,sizeof(f)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); k=cnt=num=0; while(m--) { cin>>x>>y>>w; add(x,y,w); add(y,x,w); } int c=0; for(int i=1; i<=n; i++) { if(!dfn[i]) { c++; dfs(i,-1); } } int ans=inf; for(int i=0; i<k; i+=2) { int u=e[i].u; int v=e[i].v; int w=e[i].w; if(belong[u]!=belong[v])//满足此条件的是割边 ans=min(ans,w); } if(c>1) ans=0;//不连通 else if(ans==0) ans=1;//没有人把守也要派一个人去才能炸掉桥 else if(ans>=inf) ans=-1;//不存在割边 cout<<ans<<endl; } }
相关文章推荐
- Java首课
- hihocoder-第六十一周 Combination Lock
- OpenCV学习:HOG+SVM物体分类
- linux上安装redis数据库,并实现 java连接redis一路遇到的错误
- HDU 2608 0 or 1 简单数论
- hadoop yarn 实战错误汇总
- 浅谈阅读LINUX内核源码
- Mac du笔记
- CCF 201503-4 网络延时 (树的直径)
- LeetCode || Add Digits
- hadoop yarn 实战错误汇总
- python__文件
- RBAC权限管理
- 11.6 Matlab MEX 文件的编写与调试
- rmp 安装LNMP环境
- [HDU 3306] Another kind of Fibonacci · 矩阵快速幂
- ubuntu发出奇怪的相声
- TMS320F2812启动过程
- iptables端口转发命令
- mysql 中文乱码总结