1. 程式人生 > 其它 >洛谷P1352—沒有上司的舞會(樹形DP)

洛谷P1352—沒有上司的舞會(樹形DP)

樹形DP就是在樹的基礎上做動態規劃
樹形DP有兩個方向:
1.葉->根,回溯是從葉子結點往上更新
2.根->葉,往往是在從葉往根dfs一遍之後(相當於預處理),再重新往下獲取最後的答案。

題意

某大學有 n 個職員,編號為 1…n。
他們之間有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。
現在有個週年慶宴會,宴會每邀請來一個職員都會增加一定的快樂指數 R[i] ,但是呢,如果某個職員的直接上司來參加舞會了,那麼這個職員就無論如何也不肯來參加舞會了。
所以,請你程式設計計算,邀請哪些職員可以使快樂指數最大,求最大的快樂指數。

思路

這道題是樹形DP中的典中典,從葉子往根回溯。
建樹:根據上司和員工的從屬關係建立一個有向樹
每個結點有一個價值,父節點和子節點不能同時選。
每個結點都是選和不選兩種選擇。
狀態轉移陣列:

dp[u][0]為u結點不去,獲得的子樹的最大價值
dp[u][1]為u結點去,獲得的子樹d餓最大價值

可以看出一共有三種情況和對應的狀態轉移方程
1.上司去,員工不去dp[u][1] = r[u] + dp[v][0]
2.上司不去,員工去dp[u][0] = dp[v][1]
3.上司不去,員工不去dp[u][0] = dp[v][0]
第二種和第三種情況合併dp[u][0] = max{dp[v][1],dp[u][0]}

對根節點進行dfs,最終答案為max{dp[root][0],dp[root][1]}

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAX_N = 6010; //最多結點數
int tot;    //標記邊的序號
int head[MAX_N],nxt[MAX_N],ver[MAX_N];  //建樹要用到的陣列
int r[MAX_N],flag[MAX_N];   //r為價值,flag標記u是否有上司
int dp[MAX_N][2];
void addedge(int u,int v){  //根據鄰接表建樹的過程
    ver[++tot] = v;     //tot條邊指向的點為v
    nxt[tot] = head[u]; //nxt儲存以u為始點的下一條邊的序號
    head[u] = tot;      //head[u]儲存以u為始點的邊的序號
}

void dfs(int u){
    dp[u][0] = 0;   //初始化u不去得到的dp
    dp[u][1] = r[u];    //初始化u去得到的dp
    for(int i = head[u]; i;i = nxt[i]){    //搜尋u的所有孩子
        int v = ver[i];
        dfs(v);
        dp[u][1] += dp[v][0];
        dp[u][0] += max(dp[v][0],dp[v][1]);
    }
}
int main(){
	int n;
    cin >> n;
	for(int i = 1;i <= n;i++)
        cin >> r[i];
    int u,v;
    for(int i = 1;i <= n-1;i++){
        cin >> v >> u;
        addedge(u,v);
        flag[v] = 1;    //v有上司
    }
    int root;
    for(int i = 1;i <= n;i++){  //尋找根節點
        if(!flag[i]){
            root = i;
            break;
        }
    }
    dfs(root);  //從根節點開始dfs
    cout << max(dp[root][0],dp[root][1]) << endl;   //根節點也有選和不選兩種選擇
	return 0;
}

作者:inss!w! 出處:https://www.cnblogs.com/Hfolsvh/ 版權宣告:本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!