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;
}