题目
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是 i,体积是 v i v_i vi,价值是 w i w_i wi,依赖的父节点编号是 p i p_i pi。物品的下标范围是 1…N。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数
v
i
,
w
i
,
p
i
v_i,w_i,p_i
vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果
p
i
=
−
1
p_i=−1
pi=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
1
≤
N
,
V
≤
100
1≤N,V≤100
1≤N,V≤100
1
≤
v
i
,
w
i
≤
100
1≤v_i,w_i≤100
1≤vi,wi≤100
父节点编号范围:
内部结点:
1
≤
p
i
≤
N
1≤p_i≤N
1≤pi≤N;
根节点
p
i
p_i
pi=−1;
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例:
11
思路讲解
看到一个题目,我们首先需要尝试用暴力的思想去想。
暴力思路:dfs遍历每一个点,对儿子节点进行取或者不取操作。
求最大值,那么我们就会想到使用DP,我们尝试使用DP将暴力进行优化,DP的精髓在于用一个集合代表一整个相关类型,从而达到减少时间复杂度的效果。
注意到,我们的体积是有限的,如果使用DFS的话,也是要求出它儿子节点所用的体积,那么根据这个性质,我们就可以用体积代表儿子节点所在路线的值。也就是以体积作为划分方式。
- 闫氏DP分析法
集合表示:从前i个儿子节点中选,总体积不超过j且父节点是u的值。(由于依赖前一个点是否被选,所以需要采用递归的方式)
属性: MAX
状态划分:
根据这个状态划分图,我们就可以知道这只不过是加了一个父节点的分组背包的问题。
这里有两种状态转移方程,一个是闫老师的,另一个是我自己的。
yls是将父节点空间提前腾出来,遍历出结果之后,然后再加上父节点的值。
我的是直接在状态转移方程上面直接加上父节点的值,需要注意的时,父节点只能加一次,从哪一个点转移过来的也是需要仔细分析的。
状态划分图画出来之后不能着急,需要仔细分析所要比对的值。
AC代码(预留父节点位置)
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(x) cout << #x << " = " << x << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 100 + 10;
int f[N][N], h[N], ne[N], e[N], idx, w[N], v[N];
int n, m;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u)
{
for (int i = h[u]; ~i; i = ne[i])
{
int son = e[i];
dfs(son);
For(j, m - v[u], 0) _rep(k, 0, j) f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
}
For(i, m, v[u]) f[u][i] = f[u][i - v[u]] + w[u];
_for(i, 0, v[u]) f[u][i] = 0;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(h, -1, sizeof h);
int rt = 0;
_rep(i, 1, n)
{
int c;
cin >> v[i] >> w[i] >> c;
if (c == -1) rt = i;
else add(c, i);
}
dfs(rt);
cout << f[rt][m] << ENDL;
return 0;
}
AC代码(不预留位置)
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(x) cout << #x << " = " << x << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 100 + 10;
int f[N][N], h[N], ne[N], e[N], idx, w[N], v[N];
int n, m;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u)
{
bool first = true;
for (int i = h[u]; ~i; i = ne[i])
{
int son = e[i];
dfs(son);
For(j, m, v[u]) _rep(k, 0, j - v[u]) {
f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]+ (first ? w[u] : 0));
}
if (first) first = false;
}
if (h[u] == -1) For(i, m, v[u]) f[u][i] = w[u];
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(h, -1, sizeof h);
int rt = 0;
_rep(i, 1, n)
{
int c;
cin >> v[i] >> w[i] >> c;
if (c == -1) rt = i;
else add(c, i);
}
dfs(rt);
cout << f[rt][m] << ENDL;
return 0;
}