1.割点和割边:
割点:在无向图中,删除某个节点后,图的连通分量数量增加,则称该节点为割点
桥:如果删除某条边后,连通图变得不再连通,则此条边为桥,或者为割边
下面说一种用DFS求割点的算法,即利用了“深度优先搜索生成树”求割点。
如何判断一个点是否为割点呢?
(1)如果这个点为搜索出发的点(根节点)如果他有两个及两个以上的路出发,那么这个根节点即是割点。
(2)如果他不是根节点,那么我们就要判断通过他继续往下搜索是否有路可以返回他的祖先(不能通过他的父节点)
这里主要用了两个数组来实现以上操作,即low[]和num[],low记录了此数组能返回的最早的结点,num即为进入这个递归的顺序,专业点好像叫时间戳。
设u的后代为v
判断割点的条件为low[v]>=num[u]
判断割边的条件为low[v]>num[u]
第一题poj1144
POJ1144
这个题的意思其实就是让你求一个图的割点
#pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <math.h>
#include <string>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <stdlib.h>
#define maxn 1000005
//#define true false
//#define false true
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
const double pi = acos(-1);
typedef long long ll;
const int mod = 1e9 + 7;
using namespace std;
int num[110],low[110];
vector < vector <int> > maps;
bool visited[110];
int dfsnum=0,ans;
void init(){ //初始化每组数据
memset(num,0,sizeof(num));
memset(low,0,sizeof(low));
memset(visited,false,sizeof(visited));
dfsnum=0,ans=0;;
maps.clear();
maps.resize(110);
}
void dfs(int u,int fa){
num[u]=low[u]=++dfsnum; //用一个dfsnum来记录每个点进入递归的顺序
int child=0; //一个点的孩子数目
for(int i=0;i<maps[u].size();i++){ //遍历u经过的所有子节点
int v=maps[u][i];
if(num[v]==0){ //如果没有经过此点
child++;
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=num[u]&&u!=1){
visited[u]=true;
}
} //如果经过了此点,那么判断通过这个点的父节点的low值是否可以更新为更小的
else if(num[v]<num[u]&&v!=fa) low[u]=min(low[u],num[v]);
}
if(u==1&&child>=2) visited[u]=true; //出发点的特判
}
int main()
{
int n,x,y;
while(cin>>n&&n){
init();
while(scanf("%d",&x)&&x){
while(getchar()!='\n'){
scanf("%d",&y);
maps[x].push_back(y);
maps[y].push_back(x);
}
}
dfs(1,-1); //从1开始出发
for(int i=1;i<=n;i++) ans+=visited[i];
cout<<ans<<endl;
}
return 0;
}
2.无向图的双连通分量
这个题作起来需要深度优先搜索生成树的知识点,然后计算的时候利用“缩点”的技术来实现的。缩点很好理解百度一下你就知道
第二题poj3352:
POJ3352
题目大意:就是给定一个无向图,没有重边。问添加几条边才能使无向图变为双连通图。
#pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <math.h>
#include <string>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <stdlib.h>
#define maxn 20010
//#define true false
//#define false true
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
const double pi = acos(-1);
typedef long long ll;
const int mod = 1e9 + 7;
using namespace std;
int n,m;
vector < vector<int> > maps;
int low[maxn];
int degree[maxn];
int dfsnum=0;
void init(){
memset(low,0,sizeof(low));
memset(degree,0,sizeof(degree));
dfsnum=0;
maps.clear();
maps.resize(1010);
}
void dfs(int u,int fa){
low[u]=++dfsnum;
for(int i=0;i<maps[u].size();i++){
int v=maps[u][i];
if(v==fa) continue;
if(!low[v]) dfs(v,u);
low[u]=min(low[u],low[v]);
}
}
int tarjan(){
for(int i=1;i<=n;i++){
for(int j=0;j<maps[i].size();j++){ //计算缩点的度数
if(low[i]!=low[maps[i][j]]) degree[low[i]]++;
}
}
int res=0;
for(int i=1;i<=n;i++) if(degree[i]==1) res++; //统计缩点为1的个数
return res;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
init();
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
maps[x].push_back(y);
maps[y].push_back(x);
}
dfs(1,-1);
int ans=tarjan();
printf("%d\n",(ans+1)/2);
}
return 0;
}