【TOJ 3750】Building Roads【樹形DP】

題意:給出一棵樹,樹的邊有邊權,你可以把一條邊接到其他地方,但是要保證最後還是一棵樹,使得這棵樹的直徑最小。

思路:很容易想到,我們操作的邊肯定是原圖中直徑的邊,我們先找出原圖直徑的邊,然後依次判斷操作這條邊能獲得的新圖的直徑,從而更新答案。顯然要操作的邊把樹分成了兩棵樹,要想最後還是一棵樹,這條邊肯定是接到這兩棵樹的點上,我們枚舉這個端點,可以利用樹形dp求出接完邊後的直徑的長度,對於這兩棵子樹我們要處理出兩個dp值,假設我們對這兩棵子樹已經用dfs將其轉化成有根樹,拿一棵子樹來講,dp1[i]表示i節點與非i的孩子子樹的點所能構成的最長鏈的長度,dp2[i][0]表示i節點與i的孩子子樹的節點所能構成的最長鏈的長度,dp2[i][1]表示i節點與i的孩子子樹的節點所能構成的次長鏈的長度。我們對於這兩棵樹的點進行枚舉,假設枚舉的點爲u,v,那所形成的最長鏈的長度爲:

max(dp1[u]+dp2[u][0],dp1[v]+dp2[v][0], 兩棵子樹的直徑).然後在所有最長鏈中取最小值就是答案。

現在講轉移方程:如果v不是u與u的孩子字數的節點所能構成的最長鏈上的點,dp1[v]  = max(dp1[u], dp2[v][0]),否則dp1[v] = max(dp1[u], dp2[v][1])  (v爲u的孩子)

dp2的轉移直接dfs就可以轉移了。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define N 3000

struct E{
    int v, d, ne;
    E(){}
    E(int _v, int _d, int _ne):v(_v), d(_d), ne(_ne){}
}e[N*2];

bool vis[N];
int size, ca = 1, head[N], dis[N], ans;
int pre[N], dp1[N], dp2[N][2], id[N][2], D[N];
queue<int>Q;
vector<int>V[2];

void init() {
    size = 0;
    memset(head, -1, sizeof(head));
}

void add(int u, int v, int d) {
    e[size] = E(v, d, head[u]);
    head[u] = size++;
}

int spfa(int u) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    while (!Q.empty()) Q.pop();
    dis[u] = 0, vis[u] = true;
    Q.push(u);
    int i, v, re = u, mx = 0;
    while (!Q.empty()) {
        u = Q.front(), Q.pop();
        vis[u] = false;
        if (mx < dis[u]) mx = dis[u], re = u;
        for (i = head[u];~i;i = e[i].ne) {
            v = e[i].v;
            if (dis[v] > dis[u]+e[i].d) {
                dis[v] = dis[u]+e[i].d;
                pre[v] = u;
                if (!vis[v]) {
                    Q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    ans = mx;
    return re;
}

void dfs2(int u, int f, int c) {
    V[c].push_back(u);
    int i, v, d;
    dp2[u][0] = 0, id[u][0] = u;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v, d = e[i].d;
        if (v == f) continue;
        dfs2(v, u, c);
        int ttm = dp2[v][0], ttp = dp2[v][1];
        if (ttm != -1 && ttm+d > dp2[u][0]) {
            dp2[u][1] = dp2[u][0], id[u][1] = id[u][0];
            dp2[u][0] = ttm+d, id[u][0] = v;
        }else if (ttm != -1 && ttm+d > dp2[u][1]) {
            dp2[u][1] = ttm+d, id[u][1] = v;
        }
    }
}

void dfs1(int u, int f) {
    int v, i, d;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v, d = e[i].d;
        if (v == f) continue;
        dp1[v] = dp1[u]+d;
        if (id[u][0] == v && dp2[u][1] != -1) {
            dp1[v] = max(dp1[v], dp2[u][1]+d);
        }else {
            dp1[v] = max(dp1[v], dp2[u][0]+d);
        }
        D[v] = max(dp2[v][0],dp1[v]);
        dfs1(v, u);
    }
}

void cal(int u, int v) {
    memset(dp1, -1, sizeof(dp1));
    memset(dp2, -1, sizeof(dp2));
    memset(id, -1, sizeof(id));
    V[0].clear(), V[1].clear();
    dp1[u] = dp1[v] = 0;
    dfs2(u, v, 0), dfs2(v, u, 1);
    D[u] = dp2[u][0], D[v] = dp2[v][0];
    dfs1(u, v), dfs1(v, u);
    int i, j, d;
    for (i = head[u];~i;i = e[i].ne) {
        if (e[i].v == v) break;
    }
    d = e[i].d;
    int mx = 0;
    for (i = 0;i < V[0].size();i++) {
        u = V[0][i];
        mx = max(dp1[u]+dp2[u][0], mx);
    }
    for (j = 0;j < V[1].size();j++) {
        u = V[1][j];
        mx = max(dp1[u]+dp2[u][0], mx);
    }
    for (i = 0;i < V[0].size();i++) {
        for (j = 0;j < V[1].size();j++) {
            u = V[0][i], v = V[1][j];
            int tm = max(D[u]+D[v]+d, mx);
            ans = min(tm, ans);
        }
    }
}

int main() {
    int T, i, j, u, v, d, n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        init();
        for (i = 1;i < n;i++) {
            scanf("%d%d%d", &u, &v, &d);
            add(u, v, d), add(v, u, d);
        }
        u = spfa(spfa(0));
        while (pre[u] != -1) {
            v = pre[u];
            cal(u, v);
            u = v;
        }
        printf("Case %d: %d\n", ca++, ans);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章