求樹的直徑兩種方法/樹形dp學習
阿新 • • 發佈:2021-07-10
失蹤了幾個月 我再次回來學習演算法了。感覺有點來不及了QAQ
希望自己繼續努力吧 加油少年!相信自己 擁有無限可能!!!
兩種方法求樹的直徑
何為樹的直徑?直徑既是數值概念,又指的是路徑,一般初學我們要學習的是求如何求直徑的長度
怎麼樣去求一棵樹的直徑呢?
- 任取一個點作為起點,找到距離該點距離最大的一個點u
- 再找到距離u最遠的點v
- 則u與v間的路徑就是一條直徑
為什麼這個做法是正確的呢?
事實上 我們只要證明u一定是某條直徑的一個端點,那麼很顯然就可以說明u與v之間的路徑是一條直徑
證明過程
假設已知BC為直徑(當然畫的不太像原諒我
u是距離A最遠的點
情況一:直徑與au不相交(注在這份圖中D點就是u
顯然① ≥ ② + ③;
所以① +②≥③
此時u即為直徑的端點
情況二:直徑與au相交
由定義可知②≥① 顯然u也是直徑的端點
綜上,uv一定是直徑得證。
對於樹的直徑 有兩種做法 一種是搜尋(深搜廣搜其實更推薦bfs)還有就是dp
兩次dfs求直徑
dfs就是按照上面的做法直接做就好了
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 10010, M = N * 2, INF = 0x3f3f3f3f; int n,m; int h[N], e[M], w[M], ne[M], idx,ans; int d[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } void dfs(int u, int father) { if(d[u] > ans) { ans = d[u]; m = u; } for (int i = h[u]; i != -1; i = ne[i]) {int j = e[i]; if (j == father) continue; d[j] = d[u] + w[i]; dfs(j,u); } return ; } int main() { cin >> n; memset(h, -1, sizeof h); for (int i = 0; i < n - 1; i ++ ) { int a, b, c; cin >> a >> b >> c; add(a, b, c), add(b, a, c); } dfs(1,-1); ans = 0; d[m] = 0; dfs(m,-1); printf("%d\n", ans); return 0; }
樹形dp求直徑
把所有直徑(路徑概念)都算一下,找個最大值
直徑直接列舉太複雜,不如列舉點,看作是一個點把直徑提起來,那麼我只要算兩邊的長度就好了。
找到一個最大路徑和次大路徑 所構成的就是最長路徑
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010; const int M = N << 1; int h[N],ne[M],e[M],w[M],idx,ans; int n; inline void add(int a,int b,int c) { e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++; } int dfs(int u,int father) { int d1 = 0,d2 = 0; for(int i = h[u];i != -1;i = ne[i]) { int j = e[i]; if(j == father) continue; int dist = dfs(j,u) + w[i]; if(dist > d1) { d2 = d1,d1 = dist; } else if(dist > d2) { d2 = dist; } } ans = max(ans,d1 + d2); return d1; } signed main() { memset(h,-1,sizeof h); cin >> n; for(int i = 1; i < n; ++ i) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs(1,-1); cout << ans << endl; return 0; }