一種很簡潔的寫法,那個用棧存的算法實在是比較難看。
問題大概是這樣子的:
無向圖割點:比較特殊,因為可能子節點從別的路返回到父親點,如果父親點再從自己返回到更高的父親點。。所以
low[father] =
minst{
dfn[father],
low[child],
dfn[others]
};
無向圖割邊:還可以用上面的公式,對于邊u->v,如果low[v] < dfn[u],我們可以認為這是割邊
無向圖邊雙聯通:
low[father] =
minst{
dfn[father],
low[others],除了返回父親的邊
}
至于為什么會這樣,畫個圖理解下就好
有向圖強聯通:
和無向圖邊雙聯通是一樣的,這是不對的!!還沒想到好的辦法,還得用tarjan算法搞。無向圖點雙聯通:
用求割點的那個公式,dfs把點放到棧里面,當dfn[u] == low[u]的時候出棧,棧里面的點在一個點雙聯通里,記得把當前點留在棧里
//無向圖邊雙連通+縮點,已知圖是個聯通圖
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 5010;
int nmap[maxn][maxn];
vector<int> edg[maxn];
int dfn[maxn],vis[maxn],low[maxn];
int deg[maxn];
int N,M;
int t;
void dfs(int p,int i)
{
vis[i] = 1;
dfn[i] = low[i] = ++t;
for(int j = 0;j<edg[i].size();j++)
{
int next = edg[i][j];
if(next == p) continue;
if(!vis[next])
{
dfs(i,next);
}
low[i] = min(low[i],low[next]);
}
vis[i] = 2;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d",&N,&M);
int i,a,b;
for(i=0;i<M;i++)
{
scanf("%d%d",&a,&b);
if(nmap[a][b]) continue;
edg[a].push_back(b);
edg[b].push_back(a);
nmap[a][b] = 1;
}
dfs(1,1);
int leaf = 0;
for(i=1;i<=N;i++)
{
for(int j=0;j<edg[i].size();j++)
{
if(low[i] != low[edg[i][j]])
deg[low[i]]++;
}
}
for(i=1;i<=t;i++)
{
if(deg[i] == 1) leaf++;
}
printf("%d\n",(leaf+1)/2);
return 0;
}