1. 程式人生 > >hdoj1561The more, The Better(樹形dp,依賴揹包)

hdoj1561The more, The Better(樹形dp,依賴揹包)

題意:ACboy很喜歡玩一種戰略遊戲,在一個地圖上,有N座城堡,每座城堡都有一定的寶物,在每次遊戲中ACboy允許攻克M個城堡並獲得裡面的寶物。但由於地理位置原因,有些城堡不能直接攻克,要攻克這些城堡必須先攻克其他某一個特定的城堡。你能幫ACboy算出要獲得儘量多的寶物應該攻克哪M個城堡嗎?

分析:

分類:樹形dp入門,依賴揹包

做這題前又看了一遍揹包9講,感覺太經典了,尤其是泛化揹包,簡直是精華。這題的關係就是裸地依賴揹包,用樹形dp解。

首先,限制條件是選擇m個物品,而每個物品最多選一次,跟0-1揹包的區別在於有依賴關係,那麼這層依賴關係我們可以藉助於一個樹來解決。藉助dfs,從根節點開始dfs,然後直到葉子節點,回朔的時候進行0-1揹包dp。

定義狀態:dp [ i ] [ j ] 表示在節點i,從以i為根節點的子樹下選擇j個城市的最大價值

初始化:dp [ i ] [ j ] =val [ i ](i節點的價值)(1 < = j < = m)

轉移方程 dp【father】【j】 = max (dp【father】【j】,dp【father】【k】+dp【child】【j-k】);由前面的dfs可見,我們是用子節點更新父節點,用j列舉父節點選擇的城市數,k列舉留給其他子節點選擇城市數,那麼就可以轉移了

注意:此題出給很多初始節點,也就是有很多森林,我們要設定一個超級root連線子樹的根節點即可。

程式碼:

#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 150;
using namespace std;
int n,m;
int dp[N][N],vis[N];  //dp[i][j]表示在節點i,從以i為根節點的子樹下選擇j個城市的最大價值
int cap[N],val[N];
vector<int> v[N];

void creat(int o)
{
    for(int i=0; i<v[o].size(); i++)
    {
        int t=v[o][i];
        if(vis[t] == 0 && v[t].size()>0)
            creat(t);
        for(int j = m ; j > 1 ; j--)   //j>1表示此節點一定要取 0-1揹包
        {
            for(int k=1; k<j; k++) //列舉給當前節點的其他子樹留多少可選擇的城市
                dp[o][j]=max(dp[o][j],dp[o][k]+dp[t][j-k]);
        }
    }
}

int main(){
    while(cin >> n >> m , n&&m){
        m ++; // 加一個根節點
        Del(dp,0);
        for(int i = 1 ; i <= n ; i ++){
            int a , b;
            cin >> a >> b;
            v[a].push_back(i);
            for(int j = 1 ; j <= m ; j++)
                dp[i][j] = b;  // 初始化時,每個節點,所有狀態都是拿自己一個
        }
        creat(0);
        for(int i = 0 ; i <= n ; i ++)
            v[i].clear();
        cout << dp[0][m] << endl;
    }
    return 0;
}