[O(N)的我不会]树网的核
【題目描述】
設T=(V, E, W) 是一個無圈且連通的無向圖(也稱為無根樹),每條邊帶有正整數的權,我們稱T為樹網(treenetwork),其中V, E分別表示結點與邊的集合,W表示各邊長度的集合,并設T有n個結點。
路徑:樹網中任何兩結點a,b都存在唯一的一條簡單路徑,用d(a,b)表示以a,b為端點的路徑的長度,它是該路徑上各邊長度之和。我們稱d(a,b)為a,b兩結點間的距離。
一點v到一條路徑P的距離為該點與P上的最近的結點的距離:
d(v,P)=min{d(v,u),u為路徑P上的結點}。
樹網的直徑:樹網中最長的路徑稱為樹網的直徑。對于給定的樹網T,直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。
偏心距ECC(F):樹網T中距路徑F最遠的結點到路徑F的距離, 即 ECC(F)=max{d(v,F),v∈V}.
任務:對于給定的樹網T=(V, E,W)和非負整數s,求一個路徑F,它是某直徑上的一段路徑(該路徑兩端均為樹網中的結點),其長度不超過s(可以等于s),使偏心距ECC(F)最小。我們稱這個路徑為樹網T=(V,E,W)的核(Core)。必要時,F可以退化為某個結點。一般來說,在上述定義下,核不一定只有一個,但最小偏心距是唯一的。
下面的圖給出了樹網的一個實例。圖中,A-B與A-C是兩條直徑,長度均為20。點W是樹網的中心,EF邊的長度為5。如果指定s=11,則樹網的核為路徑DEFG(也可以取為路徑DEF),偏心距為8。如果指定s=0(或s=1、s=2),則樹網的核為結點F,偏心距為12。
【輸入格式】
輸入文件包含n行:
第1行,兩個正整數n和s,中間用一個空格隔開。其中n為樹網結點的個數,s為樹網的核的長度的上界。設結點編號依次為1, 2, ..., n。
從第2行到第n行,每行給出3個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7”表示連接結點2與4的邊的長度為7。
所給的數據都是正確的,不必檢驗。
【輸出格式】
輸出文件只有一個非負整數,為指定意義下的最小偏心距。
【樣例輸入】
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【樣例輸出】
5
【分析】
首先找到一個樹的直徑。方法是:從1點出發,找到距離1最遠的點,記為x。然后從x出發,所搜索到的最遠路徑就是樹的直徑。
然后我們用搜索的方法計算每個點到直徑的最短距離dist。
之后在直徑上枚舉路徑的起點和終點。找到最小的偏心距。
#include <stdio.h> #include <string.h> #include <limits.h> #define MAXN 310 struct tnode {int num;tnode *next; } g[MAXN],*t; int q[MAXN * 3],list[MAXN],loc[MAXN],dist[MAXN],from[MAXN],dis[MAXN],di[MAXN][MAXN],xdis[MAXN][MAXN]; int n,l,r,s,x,y,z,tot,ans; bool v[MAXN],in_tree[MAXN]; void insert(int x,tnode &p) {t = new(tnode);t->num = x;t->next = p.next;p.next = t; } void dfs(int father,int x) {tnode *tt;tt = g[x].next;while (tt != NULL) {int y = tt->num;if (!in_tree[y])if (dist[x] + di[x][y] < dist[y]) {dist[y] = dist[x] + di[x][y];from[y] = father;dfs(father,y);}tt = tt->next;} } int main() { scanf("%d%d",&n,&s);for (int i = 1;i < n;++i) {scanf("%d%d%d",&x,&y,&z);insert(x,g[y]);insert(y,g[x]);di[x][y] = z;di[y][x] = z;}q[0] = 1;v[1] = 1;while (l <= r) {x = q[l];t = g[x].next;while (t != NULL) {y = t->num;if (y != from[x])if (dis[y] < di[x][y] + dis[x]) {dis[y] = di[x][y] + dis[x];from[y] = x;if (!v[y]) {q[++r] = y;v[y] = 1;}}t = t->next;}++l;v[x] = 0;}z = 0;for (int i = 1;i <= n;++i)if (dis[i] > z) {z = dis[i];x = i;}memset(from,0,sizeof(from));memset(v,0,sizeof(v));v[x] = 1;memset(dis,0,sizeof(dis));q[0] = x;v[x] = 1;l = r = 0;while (l <= r) {x = q[l];t = g[x].next;while (t != NULL) {y = t->num;if (from[x] != y)if (dis[y] < di[x][y] + dis[x]) {dis[y] = di[x][y] + dis[x];from[y] = x;if (!v[y]) {q[++r] = y;v[y] = 1;}}t = t->next;}++l;v[x] = 0;}z = 0;for (int i = 1;i <= n;++i)if (dis[i] > z) {z = dis[i];x = i;}list[++tot] = x;while (from[x]) {x = from[x];list[++tot] = x;}for (int i = 1;i <= tot;++i) {loc[list[i]] = i;in_tree[list[i]] = 1;}for (int i = 1;i <= tot;++i)for (int j = i + 1;j <= tot;++j)xdis[i][j] = xdis[i][j - 1] + di[list[j - 1]][list[j]];memset(from,0,sizeof(from));for (int i = 1;i <= n;++i)if (!in_tree[i])dist[i] = INT_MAX;for (int i = 1;i <= tot;++i)dfs(list[i],list[i]);ans = INT_MAX;for (int st = 1;st <= tot;++st)for (int en = st;en <= tot;++en) {if (xdis[st][en] > s)break;int te_ans = 0;for (int i = 1;i <= n;++i)if (!in_tree[i]) {if (dist[i] > te_ans)te_ans = dist[i];} else {x = loc[i];if (x < st) {if (dist[i] + xdis[x][st] > te_ans)te_ans = dist[i] + xdis[x][st];}if (x > en) {if (dist[i] + xdis[en][x] > te_ans)te_ans = dist[i] + xdis[en][x];}}if (te_ans < ans)ans = te_ans;}printf("%d\n",ans);return 0; }
轉載于:https://www.cnblogs.com/sephirothlee/archive/2010/10/14/1850939.html
總結
以上是生活随笔為你收集整理的[O(N)的我不会]树网的核的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AJAX顺序输出
- 下一篇: 批量修改MSSQL架构名称