caocao's bridge:無向圖求割點或橋
阿新 • • 發佈:2018-11-15
開始想用更簡單的方法但是沒實現,只能用了二維陣列
無向圖求橋的重點就是邊(u,v)(在dfs時的父子邊)如果是橋的話有dfn[u]<low[v]
求割點是:(非本題但就是想寫了XD)
如果點u是dfs時的根,u至少有兩個子節點(當然總結點數要大於3)那他就是割點
如果不是根,有一個子節點v滿足dfn[u]>=low[v],比較好理解。
其中dfn是訪問次序,low是所在連通分量的最小dfn
還是挺坑的...(gw,gw.jpg)
1.如果開始不連通就不用派人去
2.如果連通但是橋上沒有人還要派一個人去,其他情況下相等即可(所以一直在wa)
3.這玩意可能是雙向的,雙向邊不算橋...
程式碼如下:
#include<iostream> #include<vector> #include<stack> #include<algorithm> #include<string.h> using namespace std; const int maxn = 1005; typedef vector<int> edge; vector<edge>G(maxn); int soilder[maxn][maxn] = {};//覺得麻煩就用了二維陣列... int bridgenum[maxn][maxn] = {};//如果雙向就不算橋了...還會有雙向... int low[maxn] = {}, dfn[maxn] = {}; int tim; int ans; int father[maxn] = {}; int N, M;//N island M bridge void tarjan(int u,int fa)//fa是u在dfn中的父節點 { father[u] = fa; low[u] = dfn[u] = tim++; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[v]) { tarjan(v,u);//開始寫反了... low[u] = min(low[u], low[v]); } else if (v != fa) { low[u] = min(low[u], low[v]);//因為無向圖到父節點也是有邊的,所以不能修改父節點 } } } bool cando()//是否能做到 { bool flag = 0; for (int i = 2; i <= N; i++) { if (father[i] == 0)//本來就不連通的不需要派人,輸出0 { ans = 0; return true; } int v = father[i]; if (dfn[v] < low[i]&& bridgenum[v][i]==1)//,找到橋,是說兒子不會有連結到v之前的點 { ans = min(ans, soilder[v][i]); if (ans == 0)//如果橋上沒人還要派一個人去放啊啊啊啊啊 { ans = 1; } flag = true; } } return flag; } void init() { ans = 99999999;//不讓用inf tim = 1; memset(soilder, 0, sizeof(soilder)); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(bridgenum, 0, sizeof(bridgenum)); memset(father, 0, sizeof(father));//發現G不能memset... G = vector<edge>(maxn); } int main() { int u, v,w;//結點和兵數 while (1) { cin >> N >> M; if (N == 0 && M == 0) { break; } init();//初始化 while (M--) { cin >> u >> v >> w; G[v].push_back(u); G[u].push_back(v); soilder[u][v] = soilder[v][u] = w; bridgenum[u][v]++; bridgenum[v][u]++; } tarjan(1, 0);//首個結點的父親是0 if (cando()) { printf("%d\n", ans); } else { printf("-1\n");//炸不了 } } return 0; }