件名アドレス:
https://www.acwing.com/problem/content/10/
持ってNNをNアイテムと1容量はVVですVのバックパック。アイテム間には依存関係があり、依存関係は木の形をしています。アイテムを選択する場合は、その親ノードを選択する必要があります。各アイテムの数はiiですi、ボリュームはviv_iですv私、値はwiw_iですw私、依存する親ノード番号はpi p_ip私。アイテムの添え字範囲は1、…、N 1、…、Nです。1 、…、N。アイテムの総量がバックパックの容量を超えず、合計値が最大になるように、バックパックにロードするアイテムを見つけます。最大値を出力します。
入力形式:
最初の行には2つの整数NNがありますN、VVスペースで区切られたVは、それぞれアイテムの数とバックパックの容量を示します。次はNNですN行のデータ。各行のデータはアイテムを表します。最初のiii行には3つの整数vi、wi、pi v_i、w_i、p_iがありますv私、w私、p私、スペースで区切って、それぞれアイテムのボリューム、値、および依存アイテム番号を示します。もし1 P_I = -1 - PI =p私=− 1は、ルートノードを表します。データは、すべてのアイテムがツリーを構成することを保証します。
出力形式:
最大値を表す整数を出力します。
データ範囲:
1≤N、V≤1001≤N、V≤1001≤N 、V≤1 0 0
1≤VI、WI≤1001≤v_i、w_i≤1001≤v私、w私≤1 0 0
ノード番号範囲:
内部ノード:1≤pi≤N1≤p_i≤N1≤p私≤N
ルートノードpi = − 1 p_i = −1p私=− 1
アイデアは動的計画法です。f[u] [j] f [u] [j]f[u][j]是以 u u uがツリーのルートであるサブツリーでは、ツリーのルートを含むすべてのスキームの合計ボリュームはjjを超えません。jのこれらのスキームの最大値。次に、これらのスキームはiiに従うことができますiサブツリーは分類に選択されていないため、問題は0 − 10-1になります。0−サブツリーxxごとに1つのナップサック問題x、最初に再帰的にf [x] f [x]を解きますf [ x ]、次に、サブツリーによって消費されたボリュームを列挙します(ここでは、ボリュームが0、1、...、j − vu 0、1、...、j-v_uであると見なすことができます。0 、1 、。。。、j−vに、値はf [x] [0]、f [x] [1]、...、F [x] [j − vu] f [x] [0]、f [x] [1] 、。..、f [x] [j-v_u]f [ x ] [ 0 ] 、f [ x ] [ 1 ] 、。。。、f [ x ] [ j−vに]アイテム、選択するアイテムを列挙します)f [u] [j − vu] f [u] [j-v_u]を更新しますf [ u ] [ j−vに]。一般に、この問題はグループ化バックパックを作成するようなものです。各サブツリーはグループと見なされます。このグループには、サイズの異なるアイテムがいくつかあります。そのうちの1つしか選択できません(ボリューム0 0を選択)。アイテムが0の場合、このグループのアイテムを選択しないことと同じです)。列挙f [u] f [u]f [ u ]の体積は0−10-1を模倣できます0−1バックパックは大きいものから小さいものまで列挙されているため、スペースを節約するために1つの次元しか開くことができません。コードは次のように表示されます。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N];
// 邻接表建树
int h[N], e[N], ne[N], idx;
// f[u][j]的定义是,在以u为树根的子树里选,包含u的且总体积不超过j的所有方案中,最大价值是多少
int f[N][N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 求解f[u]向量
void dfs(int u) {
// 枚举每个子树
for (int i = h[u]; i != -1; i = ne[i]) {
int son = e[i];
// 对于所有的k,递归求解f[son][k]
dfs(e[i]);
// 枚举体积
for (int j = m - v[u]; j >= 0; j--)
for (int k = 0; k <= j; k++)
f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
}
for (int i = m; i >= v[u]; i--) f[u][i] = f[u][i - v[u]] + w[u];
// 当i < v[u]的时候,树根不能取,也就不存在合法方案,赋值为0
for (int i = 0; i < v[u]; i++) f[u][i] = 0;
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof h);
int root;
for (int i = 1; i <= n; i++) {
int p;
cin >> v[i] >> w[i] >> p;
if (p == -1) root = i;
else add(p, i);
}
dfs(root);
cout << f[root][m] << endl;
return 0;
}
時間計算量O(NV 2)O(NV ^ 2)O (N V2)、スペースO(NV)O(NV)O (N V )。