树形结构 —— 树与二叉树 —— 树的中心
生活随笔
收集整理的這篇文章主要介紹了
树形结构 —— 树与二叉树 —— 树的中心
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【概述】
樹的中心問題是指:當給出 n 個結點與 n-1 條邊后,要選定一個點作為整棵樹的根結點,使得從該點到每個葉結點的最長路徑最短。
樹的中心問題主要有兩種方法:DFS/BFS 進行搜索、樹形 DP 進行狀態轉移
【DFS】
根據樹的中心問題的描述,顯然可以知道,樹的中心一定在樹的直徑上,而且趨于終點,否則它的最遠距離只會更遠。
因此,我們在利用 DFS 尋找樹的直徑的同時,對于直徑的兩個端點 st、ed,分別求其到每個點的距離 disSt[i]、disEd[i]
最后,對每個點進行更新,求最小距離? 即可
struct Edge {int to, val;int next;Edge(){}Edge(int to,int val,int next):to(to),val(val),next(next){} } edge[N]; int n; int head[N], tot; int diameter,maxx, id; int dis[N], disSt[N], disEd[N]; void addEdge(int from, int to, int val) {edge[++tot].to = to;edge[tot].val = val;edge[tot].next = head[from];head[from] = tot; } void dfs(int x, int father) {for (int i = head[x]; i != -1; i = edge[i].next) {int y = edge[i].to;int val = edge[i].val;if (y == father)continue;dis[y] = dis[x] + val;if(dis[y]>maxx){maxx = dis[y];id = y;}dfs(y, x);} } void calcDiameter(){//第一遍dfsmaxx = 0;id = 1;dfs(1, 0);int st = id;//第二遍dfsmaxx = 0;dis[st] = 0;dfs(st, 0);int ed = id;diameter = maxx; //樹的直徑for (int i = 1; i <= n; i++)disSt[i] = dis[i];dis[ed] = 0;dfs(ed, 0);for (int i = 1; i <= n; i++)disEd[i] = dis[i]; }int main() {scanf("%d", &n);memset(head, -1, sizeof(head));for (int i = 1; i <= n - 1; i++) {int x, y, val;scanf("%d%d%d", &x, &y, &val);addEdge(x, y, val);addEdge(y, x, val);}calcDiameter();int pos, minn = INF;for (int i = 1; i <= n; ++i) {if (minn > max(disSt[i], disEd[i])){minn = max(disSt[i], disEd[i]);pos = i;}}printf("%d %d\n", pos, minn);return 0; }【樹形 DP】
利用樹形 DP 求解時,我們需要維護每個點 i 到所有葉結點的最長距離 up[i],從而去更新樹的中心。
由于采用樹形 DP 的方法,在求樹的直徑時已經知道如何維護每個結點 i 到其子樹的葉結點的最長距離 dis1[i] 與次長距離 dis2[i],那么接下來就要考慮如何維護這個點向上的最遠距離 up[i]
依舊用 pos1[x] 表示 dis1[x] 在哪個點更新,pos2[x] 表示 dis2[x] 在哪個點更新,再求出樹的直徑后,再次進行一次 DFS 即可
struct Edge {int to, val;int next;Edge(){}Edge(int to,int val,int next):to(to),val(val),next(next){} } edge[N]; int n; int head[N], tot; int dis1[N], dis2[N];//分別維護第i個點的最長鏈、次長鏈 int pos1[N],pos2[N];//分別維護dis1[i]、dis2[i]從哪個點更新 int up[N];//點i到所有葉結點的最遠距離 void addEdge(int from, int to, int val) {edge[++tot].to = to;edge[tot].val = val;edge[tot].next = head[from];head[from] = tot; } void dfs(int x, int father) {for (int i = head[x]; i != -1; i = edge[i].next) {int y = edge[i].to;int val = edge[i].val;if (y == father)continue;dfs(y, x);if (dis1[y] + val > dis1[x]) {dis2[x] = dis1[x];dis1[x] = dis1[y] + val;pos2[x] = pos1[x];pos1[x] = y;} else if (dis1[y] + val > dis2[x]) {dis2[x] = dis1[y] + val;pos2[x] = y;}} } void dfsCenter(int x, int father) {for (int i = head[x]; i != -1; i = edge[i].next) {int y = edge[i].to;int val = edge[i].val;if (y == father)continue;if (pos1[x] != y)up[y] = max(dis1[x], up[x]) + val;elseup[y] = max(dis2[x], up[x]) + val;dfsCenter(y, x);} } int main() {scanf("%d", &n);memset(head, -1, sizeof(head));for (int i = 1; i <= n - 1; i++) {int x, y, val;scanf("%d%d%d", &x, &y, &val);addEdge(x, y, val);addEdge(y, x, val);}dfs(1, 0);dfsCenter(1, 0);int pos, minn = INF;for (int i = 1; i <= n; i++) {if (minn > max(up[i], dis1[i])) {minn = max(up[i], dis1[i]);pos = i;}}printf("%d %d", pos, minn);return 0; }【變形問題】
樹的中心問題,最常見的一種變型問題是:給出一棵樹 n 個點的點權與 n-1 條邊的邊權,求樹的最小代價的和,定義代價為樹中兩點距離乘以點的點權
該問題是最常見的,一般數據規模較小,利用 Floyd 算法即可解決。
int n; int G[N][N], node[N], sum[N];int main() {cin >> n;for (int i = 1; i <= n; i++) //點權cin >> node[i];for (int i = 1; i <= n - 1; i++) { //邊權int x, y, dis;cin >> x >> y >> dis;G[x][y] = dis;G[y][x] = dis;}//Floyd記錄各點間的距離for (int k = 1; k <= n; k++)for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)if (G[i][j] > G[i][k] + G[k][j])G[i][j] = G[i][k] + G[k][j];//枚舉求最小代價int minn = INF;for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++)sum[i] += G[i][j] * node[j];if (sum[i] < minn)minn = sum[i];}cout << minn << endl;return 0; }?
總結
以上是生活随笔為你收集整理的树形结构 —— 树与二叉树 —— 树的中心的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通(1006:A+B问题)
- 下一篇: YY的GCD(洛谷-P2257)