# 图的连通性

dfn[u]: 表示节点u的搜索优先级
low[u]: 表示节点u,通过其本身或其子节点能到达的最小有搜索优先级

low[u] = Min{
1. dfn[u]         其本身搜索优先级
2. Min{ low[v] }  其子节点v能到达的最小优先级
3. Min( dfn[v] )  其通过回边(u,v),其中v为u的祖先节点,的优先级
}

## 一 无向图

### 1. 割点

1. 若顶点U是根,则其必定包含两个以上的子节点. (因为若只有一个.删除了U之后,图仍然连通)

2. 若顶点U不是根, 则当 dfn[u] <= low[v] , (其中V是U的子孙节点). 因为V无法通过其本身或子孙到达U或者U更高级的点.所以删除U后,图不连通.

poj 1523 SPF

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1010;

int edge[N][N];
int n, son;

int subnet[N], dfn[N], low[N], tmpdfn;
bool vis[N];

void init(){
tmpdfn = 1; son = 0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(subnet,0,sizeof(subnet));
low[1] = dfn[1] = 1; vis[1] = true;
}
void dfs(int u){
// printf("u = %d\n", u );
for(int v = 1; v <= n; v++)
{
if( edge[u][v] ){
if( !vis[v] ){
vis[v] = true;
dfn[v] = low[v] = ++tmpdfn;
dfs( v );
low[u] = min( low[u], low[v] );
if( low[v] >= dfn[u] ){
if( u != 1 ) subnet[u]++;
else son++;
}
}
else low[u] = min( low[u], dfn[v] );
}
}
}

int main(){
//  freopen("1.in","r",stdin);
int Case = 0;
while( 1 ){
int u, v; n = 0;
scanf("%d", &u);
if( u == 0 ) break;
memset(edge,0,sizeof(edge));
scanf("%d",&v);
if( u > n ) n = u;
if( v > n ) n = v;
edge[u][v] = edge[v][u] = 1;
while(1){
scanf("%d",&u);
if( u == 0 ) break;
scanf("%d",&v);
if(u > n) n = u;
if(v > n) n = v;
edge[u][v] = edge[v][u] = 1;
}
if( Case ) puts("");
printf("Network #%d\n", ++Case);
init();
dfs(1);
if( son > 1 ) subnet[1] = son-1;
bool find = false;
for(int i = 1; i <= n; i++)
{
if( subnet[i] ){
find = true;
printf("  SPF node %d leaves %d subnets\n", i, subnet[i]+1);
}
}
if( !find ) printf("  No SPF nodes\n");
}
return 0;
}
View Code

### 2. 割边(桥)

zoj 2588 burning bridge

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;

const int N = (int)1e4+10;

struct Edge{
int v, tag, nxt;
}edge[N*20];
int n, m;
int dfn[N], low[N], dep;
bool vis[N];

map< pair<int,int>,int  > mp;
vector<int> S;

{
for(int i = head[u]; ~i; i = edge[i].nxt )
if( edge[i].v == v ){
edge[i].tag++;  return;
}
edge[idx].v = v; edge[idx].nxt = head[u]; edge[idx].tag = 0;
}
void input(){
idx = 0;
scanf("%d%d", &n,&m);
mp.clear();
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u,&v);
mp[ make_pair(u,v) ] = mp[ make_pair(v,u) ] = i+1;
}
}
void tarjan(int u,int pre)
{
vis[u] = true;
dfn[u] = low[u] = ++dep;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
if( v == pre ) continue;
if( !vis[v] )
{
tarjan( v, u );
low[u] = min( low[u], low[v] );
}
else low[u] = min( low[u], dfn[v] );
if( (low[v] > dfn[u]) && !edge[i].tag ) S.push_back( mp[make_pair(u,v)] );
}
}
void solve(){
S.clear();

memset(vis,0,sizeof(vis));
dep = 0;
tarjan(1,0);

sort(S.begin(), S.end());
int tot = (int)S.size();
printf("%d\n", tot);
for(int i = 0; i < tot; i++)
printf(i == 0 ? "%d" :" %d", S[i] );
if(tot) puts("");
}

int main(){
/// freopen("1.in","r",stdin);
int _;
scanf("%d", &_);
for(int Case = 0; Case < _; Case++)
{
if(Case) puts("");
input();
solve();
}
return 0;
}
View Code

### 3. 重连通分量(顶点重复,关键点)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

const int N = 10101;
int n, m;

struct Edge{
int u, v, nxt;
Edge(){}
Edge(int _u,int _v){
u = _u; v = _v; nxt = 0;
}
int cmp( Edge &t ){
return (u==t.u&&v==t.v)||(u==t.v&&v==t.u);
}
void print(){
printf("%d->%d ", u, v );
}
}edge[N<<4];
int dfn[N], low[N], dep;
bool vis[N];
bool gao[N][N];

stack< Edge > S;

{
edge[idx].u = u; edge[idx].v = v;
}
void input(){
idx = 0;
for(int i = 0; i < m; i++)
{
int u , v;
scanf("%d%d", &u,&v);
}
}
void tarjan(int u,int pre)
{
dfn[u] = low[u] = ++dep;
vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
if( v == pre || gao[v][u] || gao[u][v] ) continue;

gao[v][u] = gao[u][v] = true;    // 这里要注意标记边.回边也只能走一次.
S.push( Edge(u,v) );
if( !vis[v] ){
tarjan(v,u);
low[u] = min( low[u], low[v] );
}
else low[u] = min( low[u], dfn[v] ); // 回边
if( low[v] >= dfn[u] ){ //删除顶点U,子节点V所在的子树将脱离.
Edge t = Edge(u,v);
while(1)
{
Edge t1 = S.top(); S.pop();
t1.print();
if( t1.cmp( t ) ) break;
}
puts("");
}
}
}
void solve(){
memset( vis, 0, sizeof(vis));
memset( gao, 0, sizeof(gao));
dep = 0;
tarjan(1,0);
}
int main()
{
freopen("1.txt","r",stdin);
//while( scanf("%d%d", &n,&m) != EOF)
scanf("%d%d", &n,&m);
{
input();
solve();
}
return 0;
}

/*
7 9
1 2
1 3
1 6
1 7
2 3
2 4
2 5
4 5
6 7

*/
View Code

### 4. 边双连通分量

poj 3177 Redundant Paths

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 1010;

struct Node{
int v, nxt;
}edge[N*100];
int n, m;
int st[N];
int belong[N], dfn[N], low[N];
bool vis[N];
int bridge[N*10][2], nbridge, D[N];
int find(int x){ return x==st[x]?x:(st[x]=find(st[x]));}
void Union(int a,int b){
int x = find(a), y = find(b);
if( x != y ) st[x] = y;
}
{
edge[idx].v = v;
}
void input(){
idx = 0;
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u,&v); u--, v--;
}
}
void tarjan(int u,int pre,int dep){
vis[u] = true;
dfn[u] = low[u] = dep;
for(int i = head[u]; ~i; i = edge[i].nxt)
{
int v = edge[i].v;
if( v == pre ) continue;
if( vis[v] ) low[u] = min( low[u], dfn[v] );
else{
tarjan( v,u,dep+1);
low[u] = min( low[u], low[v] );
if( dfn[u] >= low[v] ) Union(u,v);
if( dfn[u] <  low[v] ){
bridge[nbridge][0] = u; bridge[nbridge++][1] = v;
}
}
}
}

int GetConnection(){
for(int i = 0; i < n; i++) st[i] = i;
nbridge = 0;
memset(vis,0,sizeof(vis));
memset(belong,-1,sizeof(belong));
tarjan(0,-1,1);

int conn = 0;
for(int i = 0; i < n; i++)
{
int k = find(i);
if( belong[k] == -1 ) belong[k] = conn++;
belong[i] = belong[k];
}
return conn;
}

void solve(){
int w = GetConnection();
memset( D, 0, sizeof(D));
for(int i = 0; i < nbridge; i++)
{
int u = bridge[i][0], v = bridge[i][1];
D[ belong[u] ]++, D[ belong[v] ]++;
}
int cnt = 0;
for(int i = 0; i < w; i++)
if( D[i] == 1 ) cnt++;
printf("%d\n", (cnt+1)/2 );
}
int main(){
while( scanf("%d%d", &n,&m) != EOF)
{
input();
solve();
}
return 0;
}
View Code

### 7. 无向图强连通分量缩点建树

hdu 4612 warm up

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>
#include<algorithm>
using namespace std;

const int N = 200010;
const int M = 1000010;

struct Edge{
int v, nxt, flag;
}edge[M<<2];
int n, m;

int dfn[N], low[N], dep, vis[N];
int id[N], conn;
bool instack[N];
int nbridge, bridge[M][2];
int maxlen;

stack<int> st;

edge[idx].v = v; edge[idx].nxt = head[u];
edge[idx].flag = 1; head[u] = idx++;
edge[idx].v = u; edge[idx].nxt = head[v];
edge[idx].flag = 1; head[v] = idx++;
}
void input(){
idx = 0;
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u,&v);
}
}

void tarjan(int u,int pre)  // 无向图，数据有回边.需要将其看做不同边.且边需要标记...
{
vis[u] = true;
dfn[u] = low[u] = ++dep;
st.push(u); instack[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
if( edge[i].flag == false ) continue;
edge[i].flag = edge[i^1].flag = false;
if( !vis[v] ){
tarjan(v,u);
low[u] = min( low[u], low[v] );
if( (dfn[u] < low[v]) ){
bridge[ nbridge ][0] = u;
bridge[ nbridge++ ][1] = v;
}
}
else if( instack[v] )
low[u] = min( low[u], dfn[v] );
}
if( dfn[u] == low[u] )
{
int t;
do{
id[t=st.top()] = conn; st.pop();
instack[t] = false;
}while(t!=u);
conn++;
}
}
void GetConnect(){
memset(vis,0,sizeof(vis));
memset(instack,0,sizeof(instack));
nbridge = 0;
conn = 0; dep = 0;
while( !st.empty() ) st.pop();
for(int i = 1; i <= n; i++)
if( !vis[i] ) tarjan(i,0);

// Debug
/* for(int i = 1; i <= n; i++)
printf("dfn[%d] = %d, low[%d] = %d\n", i,dfn[i], i,low[i]);
for(int i = 1; i <= n; i++)
printf("id[%d] = %d\n", i, id[i] );*/
}
void ReGraph(){
idx = 0;
for(int i = 0; i < nbridge; i++){
int u = id[ bridge[i][0] ], v = id[ bridge[i][1] ];
// printf("u = %d, v = %d\n", u, v );
}
}

int dfs(int u,int pre)
{
int tmp = 0;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
if( v == pre ) continue;

int d = dfs(v,u);
maxlen = max( maxlen, tmp+d );
tmp = max(tmp,d);
}
return tmp+1;
}
void solve(){
GetConnect();
ReGraph();

maxlen = 0;
dfs(0,-1);

// printf("maxlen = %d, conn = %d\n", maxlen, conn);
printf("%d\n", conn-(maxlen+1) );
}
int main(){
while( scanf("%d%d", &n,&m) != EOF  )
{
if(n + m == 0) break;
input();
solve();
}
return 0;
}
View Code

## 二 有向图

### 1. 有向图强连通分量

Tarjan( u )
{
dfn[u] = low[u] = ++tmpdfn;
Stack.push( u );
for each (u,v) in E
{
if( v is not visit )   // 此时当前边是生成树的边
{
tarjan( v );
low[u] = min( low[u], low[v] )
}
else if( v is in stack ) // 此时为回边
low[u] = min( low[u], dfn[v] )
// else 此时为 交叉边,其他连同分量上的边.
}
if( dfn[u] == low[u] )
repeat
v = stack.pop;  将v腿栈,为该连通分量的一个顶点
print v
until( u == v )
}

poj 2676 Going from u to v or from v to u?

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;

const int N = 1010;

int n, m;
struct Node{
int v, nxt;
}edge[N*100];

int dfn[N], low[N], id[N], dep;
bool vis[N], instack[N];
int graph[N][N];
int D[N], topo[N];
int conn;

stack<int> st;
{
edge[idx].v = v; edge[idx].nxt = head[u];
}
void input(){
idx = 0;
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u,&v);
}
}

void tarjan(int u,int pre)
{
st.push(u); instack[u] = true;
vis[u] = true; dfn[u] = low[u] = ++dep;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
//    if( v == pre ) continue;
if( !vis[v] ){ // 生成树的边.
tarjan(v,u);
low[u] = min( low[u], low[v] );
}
else if( instack[v] ) // 在栈中.回边.
low[u] = min( low[u], dfn[v] );
}
if( dfn[u] == low[u] ){   //顶点u为根的子树是一个强连同块
int t;
do{
id[ t=st.top() ] = conn; st.pop();
instack[t] = false; //low[t] = n;
}while( t != u );
conn++; //强连通分量增加
}
}

int TopSort(){
memset( D, 0, sizeof(D));
memset( vis, 0, sizeof(vis));
while( !st.empty() ) st.pop();
for(int i = 0; i < conn; i++){
for(int j = 0; j < conn; j++)
if(i != j) D[i] += graph[j][i];
if( D[i] == 0 ) st.push(i), vis[i] = true;
}
//  if( st.size() > 1 ) return 0;
int tot = 0;
while( !st.empty() )
{
int u = st.top(); st.pop(); vis[u] = false;
topo[tot++] = u;
//printf("TopSort:  u = %d\n", u );
for(int v = 0; v < conn; v++)
if( graph[u][v] )
{
if(u == v) continue;
D[v] -= graph[u][v];
if( D[v] == 0 && !vis[v] ) st.push(v), vis[v] = true;
}
}
if( tot < conn ) return 0;
return 1;
}
void solve(){
conn = 0; dep = 0;
while( !st.empty() ) st.pop();
memset(vis,0,sizeof(vis));
memset(instack,0,sizeof(instack));

for(int i = 1; i <= n; i++)
if( !vis[i] ) tarjan(i,0);
memset( graph, 0, sizeof(graph));
for(int u = 1; u <= n; u++)
{
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
graph[ id[u] ][ id[v] ] = 1;
}
}
if( !TopSort() ) puts("No");
else
{
//for(int i = 0; i < conn; i++)
//     printf("%d ", topo[i] ); puts("");
bool flag = true;
for(int i = 0; i < conn-1; i++)
if( graph[ topo[i] ][ topo[i+1] ] == 0 )
flag = false;
puts( flag ? "Yes" : "No");
}

}
int main(){
freopen("1.txt","r",stdin);
int T;
scanf("%d", &T);
while( T-- )
{
scanf("%d%d", &n,&m);
input();
solve();
}
return 0;
}
View Code

poj 2186 Popular Cows

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<algorithm>
using namespace std;

const int N = 10010;

struct Edge{
int v, nxt;
}edge[N*10];
int n, m;

int dfn[N], low[N], dep;
int id[N], conn, num[N];
bool vis[N], instack[N];
int D[N];
stack<int> st;

{
edge[idx].v = v; edge[idx].nxt = head[u];
}
void input()
{
idx = 0;
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u,&v);
}
}
void tarjan(int u,int pre){
st.push(u); instack[u] = true;
vis[u] = true;
dfn[u] = low[u] = ++dep;
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;

if( !vis[v] ){
tarjan(v,u);
low[u] = min( low[u], low[v] );
}
else if( instack[v] )
low[u] = min( low[u], dfn[v] );
}
if( dfn[u] == low[u] )
{
int t;
do{
id[ t=st.top() ] = conn; st.pop();
instack[t] = false;
num[ conn ]++;   // 该连通分量节点数量
}while( t != u );
conn++;
}
}
int GetConnect(){
conn = 0; dep = 0;
memset(vis,0,sizeof(vis));
memset(instack,0,sizeof(instack));
for(int i = 1; i <= n; i++)
if( !vis[i] ) tarjan(i,0);

/* for(int i = 1; i <= n; i++){
printf("dfn[%d] = %d, low[%d] = %d\n", i, dfn[i], i, low[i] );
printf("id[%d] = %d\n", i, id[i] );
}*/
return conn;
}
void solve()
{
GetConnect();

for(int u = 1; u <= n; u++)
{
for(int i = head[u]; ~i; i = edge[i].nxt )
{
int v = edge[i].v;
if( id[u] != id[v] ) D[ id[u] ]++;
}
}
int ts = 0, rs = 0;
for(int i = 0; i < conn; i++)
{
if( D[i] == 0 ) ts++, rs = i;
}
if( ts == 1 ) printf("%d\n", num[rs] );
else puts("0");

}
int main()
{
//freopen("1.txt","r",stdin);
while( scanf("%d%d", &n,&m) != EOF )
{
input();
solve();
}
return 0;
}
View Code

posted @ 2013-07-26 13:49  yefeng1627  阅读(1059)  评论(0编辑  收藏  举报