1. 程式人生 > >洛谷P1169 樹上分組揹包

洛谷P1169 樹上分組揹包

題解

第一次寫樹上分組揹包的題目。

什麼是分組揹包?

分組揹包就是將物品進行分組每組內部只能選擇一類物品。

for(int i = 1;i <= N;++i){
    for(int j = 0;j <= V;++j){
        for(int k = 0;k <= item[I];++k){
            dp[i][j] = max(dp[i][j],dp[i-1][j-v[i][k]]+w[i][k]);    
        }
    }
}
//i代表組別
//j代表容量
//k代表組內物品

在本題中的使用

dp[u][

i]表示從u出發,在u的子樹中,廣播了i個人,所獲利的最大值。
然後u的一個子樹就代表一個分組。
子樹中廣播1個使用者的最大獲利、廣播2個使用者的最大獲利、、、都可以看成是平等的item。
為了避免本組內部取到多於一個的Item,所以必須使用滾動陣列,在這裡我用的方法是使用臨時陣列,本質是一樣的。

//初始化一個空陣列,作為本次計算的儲存變數。
for(int j = 1;j <= sz+sznow;++j)
    dp[3000][j] = -inf;

for(int j = 0;j <= sz+sznow;++j){//列舉要廣播的個數
    for(int k = 0;k <= min
(j,sznow);++k){//在組內列舉改組要廣播的個數(看成單個物品) dp[3000][j] = max(dp[3000][j],dp[u][j-k]+dp[v][k]-mo); } }

程式碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int inf = 1e8;
typedef pair<int,int> pii;
const int maxn = 3005
; int n,m; int dp[maxn][maxn]; int A[maxn],C[maxn],M[maxn]; vector<pii> G[maxn]; int dfs(int u){ int sz = 0; dp[u][0] = 0; if(u > n-m){ dp[u][1] = M[u]; return 1; } for(int i = 0;i < G[u].size();++i){ pii p = G[u][i]; int v = p.first; int mo = p.second; int sznow = dfs(v); dp[3000][0] = 0; for(int j = 1;j <= sz+sznow;++j) dp[3000][j] = -inf; for(int j = 0;j <= sz+sznow;++j){ for(int k = 0;k <= min(j,sznow);++k){ dp[3000][j] = max(dp[3000][j],dp[u][j-k]+dp[v][k]-mo); } } for(int j = 0;j <= sz+sznow;++j) dp[u][j] = max(dp[u][j],dp[3000][j]); sz += sznow; } return sz; } int main(){ for(int i = 0;i < maxn;++i) for(int j= 0;j < maxn;++j) dp[i][j] = -inf; cin>>n>>m; for(int i = 1;i <= n-m;++i){ int k; scanf("%d",&k); for(int j = 0;j < k;++j){ scanf("%d%d",&A[j],&C[j]); G[i].push_back(make_pair(A[j],C[j])); } } for(int i = n-m+1;i <= n;++i) scanf("%d",&M[i]); int sz = dfs(1); for(int i = sz;i >= 0;--i){ if(dp[1][i] >= 0) return 0*printf("%d\n",i); } }