1. 程式人生 > >HDU 1561 The more, The Better(多重揹包+樹形DP)

HDU 1561 The more, The Better(多重揹包+樹形DP)

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


Input
每個測試例項首先包括2個整數,N,M.(1 <= M <= N <= 200);在接下來的N行裡,每行包括2個整數,a,b. 在第 i 行,a 代表要攻克第 i 個城堡必須先攻克第 a 個城堡,如果 a = 0 則代表可以直接攻克第 i 個城堡。b 代表第 i 個城堡的寶物數量, b >= 0。當N = 0, M = 0輸入結束。


Output
對於每個測試例項,輸出一個整數,代表ACboy攻克M個城堡所獲得的最多寶物的數量。


Sample Input
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0


Sample Output
5

13

思路:dp[i][j]表示對於第i個結點以及他的所有子節點,取j個時的最大價值。

對於每個結點,就相當於求取他的子節點若干個獲得的最大價值,即一個多重揹包問題。但由於每個子節點取的個數不同,價值並不是倍數關係的,所以不能用二進位制優化,只能直接列舉。

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string.h>
#include <vector>
#include <queue>
#include <stack>
#include <ctype.h>
using namespace std;

int n,m;
vector<int> G[1005];    //記錄i結點的子節點
int v[1005];     //記錄i結點的價值
int fa[1005];   //記錄i結點的父親
int cnt[1005];   //記錄i結點以及他的子孫節點的總個數

int dp[1005][1005];

void dfs(int s)
{
    for(int i=0;i<=n;i++)
    {
        dp[s][i]=v[s];
    }
    cnt[s]=1;
    for(int i=0;i<G[s].size();i++)    //列舉子節點
    {
        dfs(G[s][i]);
        cnt[s]+=cnt[G[s][i]];
        for(int j=cnt[s];j>1;j--)    //列舉一共取j個
        {
            for(int k=1;k<=cnt[G[s][i]]&&k<=j-1;k++)    //列舉第i個子節點取k個
            {
                dp[s][j]=max(dp[s][j],dp[G[s][i]][k]+dp[s][j-k]);
            }
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0) break;
        memset(dp,0,sizeof(dp));
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=n;i++)
            G[i].clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&fa[i],&v[i]);
            G[fa[i]].push_back(i);
        }
        dfs(0);
        cout<<dp[0][m+1]<<endl;  //這裡相當於第0個結點也取了一個,所以m+1
    }
    return 0;
}