1. 程式人生 > 其它 >AcWing有依賴的揹包問題

AcWing有依賴的揹包問題

技術標籤:dp

10.有依賴的揹包問題

題意

有 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;
}