树的直径与重心
樹的直徑與重心
或許更好的閱讀體驗
樹的直徑求解方法一
思路
先選取一個點rt作為根節點,dfs去找到一個最長路徑的點U,然后通過這個點去dfs,找到路徑最長的點V,U->V就是這課樹的直徑。
證明正確性:
假如rt在直徑上的話,最長路徑的點U一定是直徑的一個端點,這一點是顯然的。
假如rt不在直徑上,那么從這個點出發也一定可以找到一條路到達直徑上一點,接下來就如同上面一樣了,無非就是在真正的dis上再加上一段固定的value,對我們最后的直徑端點查找并無影響。
代碼
#include <bits/stdc++.h>using namespace std;typedef long long ll; const int N = 1e5 + 10;int head[N], to[N], value[N], nex[N], cnt; int n, ans, dis[N];inline ll read() {ll x = 1, s = 0; char c;c = getchar();while(c < '0' || c > '9') {if(c == '-') x = -1;c = getchar();}while(c >= '0' && c <= '9') {s = (s << 1) + (s << 3) + (c ^ 48);c = getchar();}return x * s; }void add(int x, int y, int w) {to[cnt] = y;nex[cnt] = head[x];value[cnt] = w;head[x] = cnt++; }void dfs(int rt, int fa) {for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == fa) continue;dfs(to[i], rt);dis[to[i]] = dis[rt] + value[i];} }int main() {n = read();int x, y, w;memset(head, -1, sizeof head);for(int i = 1; i < n; i++) {x = read(), y = read(), w = read();add(x, y, w);add(y, x, w);}dfs(1, 0);int u = 0, v = 0;for(int i = 1; i <= n; i++)if(dis[i] > dis[u]) u = i;memset(dis, 0, sizeof dis);dfs(u, 0);for(int i = 1; i <= n; i++)if(dis[i] > dis[v]) v = i;printf("%d %d %d\n", u, v, dis[v]);return 0; }樹的直徑求解方法二
思路
以根節點出發,去尋找到這個根節點的最長路,以及次長路,得到的兩個節點就是樹的直徑的端點。如果我們要得到直徑的數值,還應該要做一次dfs,因為我們一開始選定的根節點無法保證是不是在直徑上。
代碼
#include <bits/stdc++.h>using namespace std;typedef long long ll; const int N = 1e5 + 10;int head[N], to[N], value[N], nex[N], cnt; int n, ans, fir[N], sec[N], firnode[N], secnode[N];inline ll read() {ll x = 1, s = 0; char c;c = getchar();while(c < '0' || c > '9') {if(c == '-') x = -1;c = getchar();}while(c >= '0' && c <= '9') {s = (s << 1) + (s << 3) + (c ^ 48);c = getchar();}return x * s; }void add(int x, int y, int w) {to[cnt] = y;nex[cnt] = head[x];value[cnt] = w;head[x] = cnt++; }void dfs(int rt, int fa) {firnode[rt] = secnode[rt] = rt;for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == fa) continue;dfs(to[i], rt);if(fir[to[i]] + value[i] > fir[rt]) {//更新最長路sec[rt] = fir[rt];fir[rt] = fir[to[i]] + value[i];firnode[rt] = to[i];}else if(fir[to[i]] + value[i] > sec[rt]) {//更新次長路sec[rt] = fir[to[i]] + value[i];secnode[rt] = to[i];}} }int main() {n = read();int x, y, w;memset(head, -1, sizeof head);for(int i = 1; i < n; i++) {x = read(), y = read(), w = read();add(x, y, w);add(y, x, w);}dfs(1, 0);printf("%d %d %d", firnode[1], secnode[1], fir[1] + sec[1]);//假定我們選擇的點是直徑上的點。return 0; }樹的重心
思路
找到一個點,其所有的子樹中最大的子樹節點數最少,那么這個點就是這棵樹的重心,刪去重心后,生成的多棵樹盡可能平衡。
樹中所有點到某個點的距離和中,到重心的距離和是最小的,如果有兩個距離和,他們的距離和一樣,則這兩個點都是重心
并且,一棵樹最多有兩個重心,且相鄰。
因此我們可以通過dfs去記錄每個節點的子樹節點值,然后去統計其最大結點數最小的節點。
代碼
#include <bits/stdc++.h>using namespace std;typedef long long ll; const int N = 1e5 + 10;int head[N], to[N], value[N], nex[N], cnt; int n, ans, sz[N], maxn = 0x3f3f3f3f, heart;inline ll read() {ll x = 1, s = 0; char c;c = getchar();while(c < '0' || c > '9') {if(c == '-') x = -1;c = getchar();}while(c >= '0' && c <= '9') {s = (s << 1) + (s << 3) + (c ^ 48);c = getchar();}return x * s; }void add(int x, int y, int w) {to[cnt] = y;nex[cnt] = head[x];value[cnt] = w;head[x] = cnt++; }void dfs(int rt, int fa) {sz[rt] = 1;int now_maxn = 0;for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == fa) continue;dfs(to[i], rt);sz[rt] += sz[to[i]];if(sz[to[i]] > now_maxn) now_maxn = sz[to[i]];//記錄最大子節點。}if(n - sz[rt] > now_maxn) now_maxn = n - sz[rt];//其父節點也算是他的子節點if(now_maxn < maxn) heart = rt, maxn = now_maxn; }int main() {n = read();int x, y, w;memset(head, -1, sizeof head);for(int i = 1; i < n; i++) {x = read(), y = read(), w = read();add(x, y, w);add(y, x, w);}dfs(1, 0);printf("%d\n", heart);return 0; }總結
- 上一篇: 老鼠屎有毒吗
- 下一篇: 派瑞松软膏的作用与功效