洛谷P1169 樹上分組揹包
阿新 • • 發佈:2018-12-24
題解
第一次寫樹上分組揹包的題目。
什麼是分組揹包?
分組揹包就是將物品進行分組每組內部只能選擇一類物品。
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代表組內物品
在本題中的使用
設表示從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);
}
}