AcWingには依存するバックパック問題があります

10.依存ナップサック問題

題名

N個のアイテムとVの容量を持つバックパックがあります。

アイテム間には依存関係があり、依存関係は木の形をしています。アイテムを選択する場合は、その親ノードを選択する必要があります。
ここに画像の説明を挿入

項目5を選択する場合は、項目1と2を選択する必要があります。これは、2が5の親ノードであり、1が2の親>ノードであるためです。

各アイテムの数はi、ボリュームはvi、値はwi、依存する親ノードの数はpiです。アイテムの添え字範囲は1 ... Nです。
アイテムの総量がバックパックの容量を超えず、合計値が最大になるように、バックパックにロードされているアイテムを解決します。
最大値を出力します。

入力フォーマット

最初の行には2つの整数NとVがあり、スペースで区切られており、それぞれアイテムの数とバックパックの容量を示しています。
次に、N行のデータがあり、データの各行はアイテムを表します。
i番目の行には3つの整数vi、wi、およびpiがあり、スペースで区切られ、アイテムのボリューム、値、および従属アイテム番号を示します。
pi = -1の場合、ルートノードを意味します。データは、すべてのアイテムがツリーを構成することを保証します。

出力フォーマット

最大値を表す整数を出力します。

データ範囲

1≤N、V≤1001≤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からの添え字です。

主要部分が1つだけで、他に複数のアタッチメントがあると仮定すると、主要部分とアタッチメントの2 ^ n(順列と組み合わせ)セットが存在する可能性があります。つまり、解決策はこれらの戦略にあります。

2 ^ nを列挙することは明らかに非現実的かもしれません。「単純な最適化」、つまり01バックパックを考えることができます。主要部分が選択されているという前提の下で、01バックパックを使用して他のほとんどのアクセサリを選択します(それぞれに選択するかどうか)アクセサリ)。優れたソリューション

01バックパックの「ボリュームは同じだが価値が高い」は役に立たない戦略を除外するため、正の解決策はfn [key] [j]を使用したdp [j]のコードです。

連絡先の質問の意味(ツリー):各メインピースの添付ファイルは、別のオブジェクトのメインピースである可能性があります(質問でオブジェクトのメインピース(ルートではない)が選択されている限り、このオブジェクトを選択できます)、したがって、各添付ファイルもコレクションです。最初に独自の添付ファイルのコレクション(添付ファイルはコレクションです)を見つけることにより、添付ファイルに割り当てるさまざまなサイズを通じて、この添付ファイルにはさまざまな利点があります。

したがって、01バックパックを使用してアクセサリを選択する場合は、内部ループを追加し、アクセサリを再列挙して(アクセサリ自体もツリーです)、さまざまなボリュームに対応するメリットを割り当てます/さまざまなサイズのバックパックのメリットの最適な戦略を取得します。

では、割り当てられたさまざまなボリュームに対応する添付ファイルの最適な戦略を見つけるにはどうすればよいでしょうか。
ツリーの再帰的な性質を直接使用して、さまざまなボリュームのセットに最適な戦略が求められるたびに、さまざまなボリュームに割り当てられたアクセサリの最適な戦略を最初に見つけることができます。

コードは18ミリ秒以内に実行できます

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


おすすめ

転載: blog.csdn.net/RunningBeef/article/details/111416061