P3388 【模板】割點(割頂)
1、注意在遍歷時要儲存根節點編號,判斷時需要特判根節點
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n,m,r;
int dn,dfn[N],low[N],cnt,buc[N];
vector<int> e[N];
void dfs(int id){
//標記時間戳
dfn[id] = low[id] = ++dn;
int son = 0;
//遍歷該點相鄰點
for(int it : e[id]){
//如果沒有被遍歷
if(!dfn[it]){
//其子節點加一
son++;
//遍歷當前節點
dfs(it);
//進行更新
low[id] = min(low[id],low[it]);
//若id的子節點不能夠透過其他路徑走到比id時間戳更小的點 並且 id不為根節點
//id就為割點
if(low[it] >= dfn[id] && id != r){
//割點數量加一
cnt += !buc[id];
//將id標記為割點
buc[id] = 1;
}
}else{
//更新id_low為其鄰點的最小時間戳
//得到透過id點能夠到達的最小值
low[id] = min(low[id],dfn[it]);
}
}
//如果id是根節點,但其有兩個以上的子樹,那麼id為割點
if(son >= 2 && id == r){
//更新,標記
cnt += !buc[id];
buc[id] = 1;
}
}
int main(){
cin>>n>>m;
//鄰接表儲存連點
for(int i = 1; i <= m; ++i){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
//遍歷dfs
for(int i = 1; i <= n; ++i){
//沒有被遍歷
if(!dfn[i]){
//初始化頂點
r = i;
dfs(i);
}
}
cout<<cnt<<endl;
for(int i = 1; i <= n; ++i){
if(buc[i]){
cout<<i<<" ";
}
}
return 0;
}
洛谷 P1656 炸鐵路
讀圖,鄰接表存圖,tarjan求割邊(橋),排序,輸出
注意父親節點的判斷,如果兩點之間有重邊的情況需要注意
如果在第一次之外還回到了父親結點(說明可能有重邊),則按像計算兒子結點的方法一樣用父親結點的值更新當前結點的值。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10010;
int n, m, x, y, idx, dfn[MAXN], low[MAXN], ans,a;
//dfn儲存固定時間戳, low儲存其出點能夠到達的最小時間戳
vector<int> G[MAXN];//使用鄰接表存圖
struct Edge{int from, to;} edge[MAXN];//題目要求,a < b
bool cmp(const Edge a, const Edge b){if(a.from != b.from)return a.from < b.from; return a.to < b.to;}//題目要求排序
inline void add_edge(int x, int y){edge[ans].from = min(x, y); edge[ans].to = max(x, y); ans++;}//在答案中加入一條邊
void dfs(int cur, int fa){//cur當前節點, fa 為其父親節點
int child;
dfn[cur] = ++idx;// 計算當前節點的時間戳
low[cur] = dfn[cur];//初始化 當前可以訪問到的最早時間戳肯定是自己的時間戳
bool vis = false;//防止在有重邊時會出錯
for(int i = 0; i < G[cur].size(); ++i){//遍歷它的所有出點
child = G[cur][i];//取出出點
//如果這個節點被訪問過
if(dfn[child]){
if(child == fa && !vis) vis = true;//如果是父親節點 且是第一次訪問不做任何操作
else low[cur] = min(low[cur], dfn[child]);//如果訪問到了不是父親節點的節點 更新low值
//如果在第一次之外還回到了父親結點,則按像計算兒子結點的方法一樣用父親結點的值更新當前結點的值。
}
//如果這個節點之前沒有被訪問過
if(!dfn[child]){
dfs(child, cur);//進行dfs
if(dfn[cur] < low[child]) add_edge(cur, child);//如果滿足條件(當前節點向下走無法回到之上的節點),
//說明當邊是割邊 將其加入答案之中
low[cur] = min(low[cur], low[child]);
//更新當前節點的low值
}
}
}
int main(){
cin >> n >> m;
for(int i = 0; i < m; ++i) cin >> x >> y,G[x].push_back(y),G[y].push_back(x);
for(int i = 1; i <= n; ++i) if(!dfn[i]) dfs(i, i);//圖可能不連通, 初始時將自己的父親節點初始化為自己,不會出錯
sort(edge, edge + ans, cmp);//將答案進行排序
for(int i = 0; i < ans; ++i) cout << edge[i].from <<" "<< edge[i].to << endl;//輸出
return 0;
}