1. 程式人生 > >【樹形dp小練】HDU1520 HDU2196 HDU1561 HDU3534

【樹形dp小練】HDU1520 HDU2196 HDU1561 HDU3534

  • 【樹形dp】就是在樹上做的一些dp之類的遞推,因為一般需要遞迴處理,因此平凡情況的處理可能需要理清思路。昨晚開始切了4題,作為入門訓練。題目都非常簡單,但是似乎做起來都還口以~

hdu1520 Anniversary party

  • 給一顆關係樹,各點有權值,選一些點出來。任一對直接相連的邊連線的點不能同時選擇,問選擇出的權值和最大為多少。
  • 考慮以u為根的子樹可以選擇的方法,dp[u]表示選擇u時能獲得最大收益,dp2[u]表示不選u時能獲得的最大收益。則u不選時,為dp2[u]=max{dp[v],dp2[v]}vuu要選時,為dp[u]=valueu+max{dp2[v]
    }vu
/* **********************************************

  File Name: 1520.cpp

  Auther: [email protected]

  Created Time: 2015年08月18日 星期二 19時50分55秒

*********************************************** */
#include <bits/stdc++.h>
using namespace std;

const int INF = 0xfffffff;
const int MAX = 6007
; vector<int> G[MAX]; int val[MAX]; int dp[2][MAX]; bool vis[MAX]; int n; void dfs(int u) { //printf("dfs %d\n", u); vis[u] = true; dp[0][u] = 0; dp[1][u] = val[u]; for (auto it = G[u].begin(); it != G[u].end(); ++it) { if (!vis[*it]) { dfs(*it); dp[1
][u] += max(0, dp[0][*it]); dp[0][u] += max(0, max(dp[0][*it], dp[1][*it])); } } } int main() { while (cin >> n && n) { for (int i = 1; i <= n; ++i) { G[i].clear(); cin >> val[i]; } int x, y; for (int i = 1; i < n; ++i) { cin >> x >> y; G[x].push_back(y); G[y].push_back(x); } cin >> x >> y; //0 0 //fill(dp[0], dp[0] + n + 1, -INF); //fill(dp[1], dp[1] + n + 1, -INF); memset(vis, false, sizeof(vis)); dfs(1); //node 1 is root /* for (int i = 1; i <= n; ++i) { printf("dp[0][%d] = %d, dp[1][%d] = %d\n", i, dp[0][i], i, dp[1][i]); } */ int ans = max(dp[0][1], dp[1][1]); if (ans == 0) { ans = -INF; for (int i = 1; i <= n; ++i) { if (ans < val[i]) { ans = val[i]; } } } cout << ans << endl; } return 0; }

hdu2196 Computer

  • 一個樹型網路,每個點代表一臺Computer,每個點的MaximumDistance表示該點到樹上其它所有點的距離中最長的。求i=1nMDistancei
  • 先預處理出各點深度。然後考慮點u的代價,即點u到其它某一點的最長路徑,顯然分為經過其父親節點和不經過父親節點兩種。對於前者,我們在遍歷時傳遞一個值fd給它,表示經過父親節點到其它所有點的距離最長為多少,便可解決。對於後者,只需要考慮u到以u為根的子樹中某一葉子的最長距離即可,於是可以通過u的孩子獲得。下面說明怎麼維護上述資訊。
  • 對後一種情況,非常簡單地就可以做到:對每個點維護一個值,表示以該點為根的樹的高度,不妨表示為val1u,則val1u=max{val1v+1}vu,初始化為0.
  • 對前者,我們考慮fd首先必須包含u的父親fau的這條邊的長度,設為len1,然後有兩種延伸方式,第一種是向fa的其它若干孩紙中的一個延伸下去,第二種是向fa的父親延伸。第二種延伸顯然已經無需額外維護,之前的遞迴已經處理完畢;第一種延伸則提示我們在搜尋fa時,需要將fa得到的fdfa的其它孩紙的val1值進行對比,取較大者作為fd傳遞給u,也就是用其它孩紙的val1來動態地更新維護fd值。這樣整個問題就做完了。
  • 注意輸入
/* **********************************************

  File Name: 2196.cpp

  Auther: zhengdongjian@tju.edu.cn

  Created Time: 20150819日 星期三 083506秒

*********************************************** */
#include <bits/stdc++.h>
using namespace std;

/*
 * let dp[i][j]表示節點i的孩紙j所在子樹的最大深度.
 * dp[i][j] = max{dp[j][k]} + 1.
 * Then. dfs後繼遍歷整棵樹,父親為root,遍歷孩紙i時,
 * 傳遞max{max{dp[root][k]}(k!=i), fd}和一個深度標記。
 * 其中fd為root的父親傳遞進來的值。深度標記需要修正。
 * 此次掃描即可得出每個點的花費
 */

typedef pair<int, int> P;
typedef pair<P, int> PP;
const int MAX = 10007;
vector<P> G[MAX];
multimap<P, int> M[MAX];
int cost[MAX];
bool vis[MAX];

int dfs(int u) {
    vis[u] = true;
    int res = 0;
    for (auto it = G[u].begin(); it != G[u].end(); ++it) {
        if (!vis[it->first]) {
            int dep = dfs(it->first) + it->second;
            if (dep > res) res = dep;
            M[u].insert(PP(P(dep, it->first), it->second));
        }
    }
    return res;
}

void gao(int u, int fd) {
    //printf("gao %d, with %d\n", u, fd);
    //cost[u] = max{fd, M[u].rbegin()->first|M[u].rbegin()-1 ->first}
    cost[u] = max(fd, M[u].empty() ? 0 : M[u].rbegin()->first.first);
    //printf("cost[%d] = %d\n", u, cost[u]);
    if (M[u].empty()) {
        return;
    } else if (M[u].size() == 1) {
        auto it = M[u].begin();
        gao(it->first.second, it->second + fd);
    } else {
        for (auto it = M[u].begin(); it != M[u].end(); ++it) {
            for (auto it2 = M[u].rbegin(); it2 != M[u].rend(); ++it2) {
                if (it2->first.second != it->first.second) {
                    //printf("%d != %d\n", it2->first.second, it->first.second);
                    gao(it->first.second, it->second + max(fd, it2->first.first));
                    break;
                }
            }
        }
    }
}

int main() {
    int n;
    while (cin >> n) {
        for (int i = 1; i <= n; ++i) {
            G[i].clear();
            M[i].clear();
        }
        int x, y;
        for (int i = 2; i <= n; ++i) {
            cin >> x >> y;
            G[i].push_back(P(x, y));
            G[x].push_back(P(i, y));
        }
        memset(vis, false, sizeof(vis));
        dfs(1);
        /*
        for (int i = 1; i <= n; ++i) {
            printf("%d: ", i);
            for (auto it = M[i].begin(); it != M[i].end(); ++it) {
                printf("<%d,%d> ", it->first.second, it->first.first);
            }
            puts("");
        }
        */
        gao(1, 0);
        for (int i = 1; i <= n; ++i) {
            printf("%d\n", cost[i]);
        }
    }
    return 0;
}

hdu1561 The more, The Better

  • 首先簡單分析一下。
    • 1. 將依賴關係視為有向邊,則原關係網路為一個有向圖。
    • 2. 其次,我們發現任意一個點的入度不會大於1,也就是說,不會同時依賴於一個以上的點,原圖似乎是一種拓撲的存在。
    • 3. 但是要注意,可能出