AcWing有依賴的揹包問題
技術標籤:dp
題意
有 N 個物品和一個容量是 V 的揹包。
物品之間具有依賴關係,且依賴關係組成一棵樹的形狀。如果選擇一個物品,則必須選擇它的父節點。
如果選擇物品5,則必須選擇物品1和2。這是因為2是5的父節點,1是2的父>節點。
每件物品的編號是 i,體積是 vi,價值是 wi,依賴的父節點編號是 pi。物品的下標範圍是 1…N。
求解將哪些物品裝入揹包,可使物品總體積不超過揹包容量,且總價值最大。
輸出最大價值。
輸入格式
第一行有兩個整數 N,V,用空格隔開,分別表示物品個數和揹包容量。
接下來有 N 行資料,每行資料表示一個物品。第 i 行有三個整數 vi,wi,pi,用空格隔開,分別表示物品的體積、價值和依賴的物品編號。
如果 pi=−1,表示根節點。 資料保證所有物品構成一棵樹。
輸出格式
輸出一個整數,表示最大價值。
資料範圍
1≤N,V≤100
1≤vi,wi≤100
父節點編號範圍:
內部結點:1≤pi≤N;
根節點 pi=−1;
輸入樣例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
輸出樣例
11
題意解析:
每個物品輸入次序就是下標,從1-n
假設只有一個主件 和多個n其他附件 那麼可以有2^n(排列組合)個主件和附件的集合,也就是解在這些策略中
列舉2^n個可能明顯不現實, 我們可以考慮" 一個簡單的優化 " 也就是01揹包,在主件已經選擇的前提下用01揹包選擇其他附件(每個附件選或者不選)的最優解
因為01揹包 "體積相同,但價值高"篩選掉無用策略,所以正解就是dp[j]中的一種 程式碼中用fn[key][j]
聯絡題意(樹):每個個主件的附件可能是另一個物件的主件(題意中只要已經選了一個物件(不是根) 的主件就可以選這個物件),所以每個附件的本身也是一個集合,通過優先求出附件自己的集合(附件為集合)通過你給附件分配的不同體積,這個附件就有不同的收益
因此再用01揹包的選擇附件時,加個內部迴圈 再列舉給附件(附件自己也是一棵樹)分配不同體積對應的收益/就可以得到不同體積下的揹包收益的最優策略。
那麼如何求其附件自己對應不同的分得的到的體積的最優策略呢?
直接用樹的遞迴性質, 每次求一個集合的不同體積的最優策略 就先求出其附件對於分配到不同體積的最優策略即可。
程式碼能跑18ms以內
#include <iostream>
using namespace std;
const int N =200;
int fn[N][N];
int p[N],v[N],w[N];
int n,V;
int fun(int key,int VV){//key主件下標 VV可分配的體積
//遞迴的求01
if(VV<v[key])//沒有體積選擇這個物件
return 0;
for(int i = 1;i <= n;i++)//對附件進行遞迴
if(p[i] == key)//如果是它的附件
fun(i,VV-v[key]);
fn[key][v[key]] = w[key];//先選擇主件, 選了主件才能選擇對應的附件
for(int i = 0;i <= n;i++){//01揹包 對不同體積求 最大價值
if(p[i] == key)//如果是主件的附件
for(int j = VV;j>=v[key];j--){//從體積最大開始 確保每個附件其對應的集合只能選擇一個次
for(int j1 = 0;j1 <=j-v[key];j1++ )//可支配體積
if(fn[key][j-j1] && fn[i][j1])//當已經選了主件,並且分配給附件的體積是可以得到有意義的價值的
fn[key][j] = max(fn[key][j],fn[key][j-j1]+fn[i][j1]);//更新可能的價值
}
}
}
int main(){
cin >> n >>V;
int key;
for(int i = 1;i <= n;i++){
cin >> v[i] >> w[i] >> p[i];
if(p[i] == -1)
key = i;//獲得根結點的下標
}
fun(key,V);//結點, 和可支配的體積
int result = 0;
for(int j = 0;j <=V;j++)//從不同體積的最優策略中選擇 價值最大的策略
result = max(result,fn[key][j]);
cout << result << endl;
return 0;
}