背包问题

  • 1. 01背包问题
  • 2. 完全背包问题
  • 3. 多重背包问题
  • 4. 混合背包问题
  • 5. 二维费用的背包问题
  • 6. 分组背包问题
  • 7. 背包问题求方案数
  • 8. 求背包问题的方案
  • 9. 有依赖的背包问题

1. 01背包问题

特点:每个物品只能用一次,只能是选择或者不选择

题目链接
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

8

f [ i ][ j ] 表示只看前 i 个物品,总体积是 j 的情况下,总价值最大是多少

result = max { f [ n ][ 0 ~ v ] }

f [ i ][ j ] :
1 . 不选第 i 件物品,f [ i ][ j ] = f [ i - 1][ j ];
2 . 选第 i 件物品,f [ i ][ j ] = f [ i - 1][ j - v[ i ] ];
f [ i ][ j ] = max { 1, 2 }

f [ 0 ][ 0 ] = 0;

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int f[N][N];
int v[N], w[N];
int n, m;int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];f[0][0] = 0;for(int i = 1; i <= n; i ++ )for(int j = 0; j <= m; j ++ ){f[i][j] = f[i - 1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);}int ans = 0;for(int i = 0; i <= m; i ++ ) ans = max(ans, f[n][i]);cout << ans << endl;return 0;
}

优化为 1 维的 : f [ i ][ j ] => f [ j ]
f [ j ] 总体是 j 的情况下,总价值最大是多少

result = max { f[ 0 ~ v ] }; => result = f [ m ]

f [ j ]:
1 . 不选第 i 个物品,f [ j ] = f [ j ];
2 . 选第 i 个物品,f [ j ] = f [ j - v [ i ] ];(注意:这个时候的循环要从大到小循环了)

for(int i = 1; i <= n; i ++ )for(int j = m; j >= v[i]; j -- )f[j] = max(f[j], f[j - v[i]] + w[i]);

f [ j ] = max {1, 2};

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int f[N];
int v[N], w[N];
int n, m;int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++ )for(int j = m; j >= v[i]; j -- )f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[m];return 0;
}

2. 完全背包问题

特点:每种物品有无限个,可以选择 0~n 个

题目链接

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i 种物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

10

f [ i ] 表示总体积是 i 的情况下,最大价值是多少

result = max { f [ 0 ~ m ] };

f [ i ] :

 for(int i = 1; i <= n; i ++ )for(int j = v[i]; j <= m; j ++ )f[j] = max([j], [j - v[i]] + w[i]);

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int f[N];
int v[N], w[N];
int n, m;int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++ )for(int j = v[i]; j <= m; j ++ )f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[m];return 0;
}

3. 多重背包问题

特点:每件物品有 s 件,对于每件物品可以选择 0 ~ s 件

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<vi,wi,si≤100

输入样例:

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

f [ i ] 表示总体积是 i 的情况下,最大值是多少

f [ i ] :

for(int i = 1; i <= n; i ++ )for(int j = m; j >= v[i]; j -- )f[j] = max(f[j], f[j - 1 * v[i]] + 1 * w[i], f[j - 2 * v[i]] + 2 * w[i] ... )

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int f[N];
int v[N], w[N], s[N];
int n, m;int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[i];for(int i = 1; i <= n; i ++ )for(int j = m; j >= v[i]; j -- )for(int k = 0; k <= s[i] && k * v[i] <= j; k ++ )f[j] = max(f[j], f[j - k * v[i]] + k * w[i]);cout << f[m];return 0;
}

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

二进制优化

举个栗子:
7 -> 我们可以分解为 7 个 1 相加,但是这样分解后,时间复杂度并没有降低
-> 我们还可以分解成 1 、 2 、 4 相加,这样分解后,时间复杂度很明显就降低了,那么1 、 2 、 4 是怎么来的呢?
20 = 1;
21 = 2;
22 = 4;
其实就是 log27 向上取整得到 2 ,所以是 20 - 22 之间的数字
这就是乘法原理,利用1 、 2 、 4,我么就可以将 0 - 7 之间的所有数字表示出来了
如:
0 = 0
1 = 1
2 = 2
3 = 1 + 2
4 = 4
5 = 1 + 4
6 = 2 + 4
7 = 1 + 2 + 4
但是当 7 变成 10 呢,继续利用 log210 向上取整,得到的 3 ,也就是 1 、 2 、 4 、 8,但是此时表示的最大数值为 15 ,并不是 10 ,所以,我只选择 1 、 2 、 4,剩余的数字直接用 10 - 1 - 2 - 4 - 8 = 3 表示
1 、 2 、 4 可以是表示 0 - 7 之间的全部数字,
那么 1 、 2 、4 、 3 就可以表示 0 - 10 之间的全部数字

利用这个方法,我们就可以把数据范围为 2000 的数量变成了 log22000 ≈ 11 ,这样我们在继续实现多重背包就可以了

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int f[N];
int v[N], w[N], s[N];
int n, m;struct Good{int v, w;
};int main()
{vector<Good> good;cin >> n >> m;for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];for(int i = 0; i < n; i ++ ){// 二进制处理 for(int j = 1; j <= s[i]; j *= 2 ){s[i] -= j;good.push_back({v[i] * j, w[i] * j});}if(s[i] >= 0) good.push_back({v[i] * s[i], w[i] * s[i]});}// 多重背包for(auto goods : good)for(int j = m; j >= goods.v; j -- )f[j] = max(f[j], f[j - goods.v] + goods.w);cout << f[m] << endl;return 0;
}

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V≤20000
0<vi,wi,si≤20000

提示:

本题考查多重背包的单调队列优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

基本的多重背包问题状态转移方程:
f [ j ] = max ( f [ j ] , f [ j - k * v ] + k * w );
f [ j ] = max ( f [ j ] , f [ j - v ] + w , f [ j - 2 * v ] + 2 * w , f [ j - 3 * v ] + 3 * w … f [ j - s * v ] + s * w )
f [ j - v ] = max ( f [ j - v ] , f [ j - 2 * v ] + w , f [ j - 3 * v ] + 2 * w , f [ j - s * v ] + ( s - 1 ) * w , f [ j - ( s + 1 ) * v ] + s * w )
f [ j - 2 * v ] = …
f [ j - 3 * v ] = …
从上面的 f [ j ] 和 f [ j - v ] 我们可以看出来,相同的部分为:
f [ j - v ] + w , f [ j - 2 * v ] + 2 * w , f [ j - 3 * v ] + 3 * w … f [ j - s * v ] + s * w
f [ j - v ] , f [ j - 2 * v ] + w , f [ j - 3 * v ] + 2 * w , f [ j - s * v ] + ( s - 1 ) * w
因此我们只需要维护 f [ j - v ] 到 f [ j - s * v ] 这个区间中的最大值就可以,而维护一个区间的最大值我们就可以使用单调队列来进行维护

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=20010;int n, m;
int v[N], w[N], s[N];
int f[N], g[N], q[N];int main()
{cin >> n >> m;for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];for(int i = 0; i < n; i ++ ){// 滚动数组 memcpy(g, f, sizeof f);for(int j = 0; j < v[i]; j ++ ){int hh = 0, tt = -1;for(int k = j; k<= m; k += v[i]){if(hh <= tt && q[hh] < k - s[i] * v[i]) hh ++ ;while(hh <= tt && g[q[tt]] - (q[tt] - j) / v[i] * w[i] <= g[k] - (k - j) / v[i] * w[i]) tt -- ;q[ ++ tt] = k;f[k] = g[q[hh]] + (k - q[hh]) / v[i] * w[i]; }} } cout << f[m] << endl;return 0;
}

4. 混合背包问题

特点:包含01背包、完全背包、多重背包三种背包问题

题目链接

有 N 种物品和一个容量是 V 的背包。

物品一共有三类:

第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次;
输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000

输入样例

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

输出样例:

8

对于多重背包,我们利用二进制优化后,也就是二进制分组后,对于每一组都相当于01背包,所以在标记背包种类的时候,要标记成01背包

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int n, m;
int v[N], w[N], s[N];
int f[N];struct Good{int kind;int v, w;
};vector<Good> good;int main()
{cin >> n >> m;for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];for(int i = 0; i < n; i ++ ){if(s[i] == -1) good.push_back({-1, v[i], w[i]});else if(s[i] == 0) good.push_back({0, v[i], w[i]});// 多重背包二进制优化(分组)之后,对于每一组都相当于是01背包 else{for(int k = 1; k <= s[i]; k *= 2){s[i] -= k;good.push_back({-1, v[i] * k, w[i] * k});}if(s[i] > 0) good.push_back({-1, v[i] * s[i], w[i] * s[i]});}}// 对于01背包和完全背包分两种情况状态转移即可 for(auto goods : good){// 01背包 if(goods.kind == -1){for(int j = m; j >= goods.v; j -- )f[j] = max(f[j], f[j - goods.v] + goods.w);}else{for(int j = goods.v; j <= m; j ++ )f[j] = max(f[j], f[j - goods.v] + goods.w); } } cout << f[m] << endl;return 0;
}

5. 二维费用的背包问题

特点:一般的背包问题,只有一个容量的限制,对于二维背包问题,除了容量的限制还有另一个限制,

题目链接

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。

每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。

输入格式
第一行三个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

接下来有 N 行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V,M≤100
0<vi,mi≤100
0<wi≤1000

输入样例

4 5 6
1 2 3
2 4 4
3 4 5
4 5 6

输出样例:

8

f [ i ][ j ] 表示总体积是 i ,总重量是 j 的情况下,最大价值是多少

同时对于状态转移的时候,两层 for 循环,一层是体积限制,一层是重量限制

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int a, b, c;
int v[N], t[N], w[N];
int f[N][N];int main()
{cin >> a >> b >> c;for(int i = 0; i < a; i ++ ) cin >> v[i] >> t[i] >> w[i];for(int i = 0; i < a; i ++ )for(int j = b; j >= v[i]; j -- )for(int k = c; k >= t[i]; k -- )f[j][k] = max(f[j][k], f[j - v[i]][k - t[i]] + w[i]);cout << f[b][c] << endl;return 0;
}

6. 分组背包问题

特点:每一组中有多个物品,但是同组内的物品最多只能选择一个

题目链接

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例:

8

对于不同组之间来说就相当于普通的背包问题,但是在同一组中,就只能选择一个物品,也就是说,选择了第一组中的第一个物品,那么第一组中的其他物品就不能够再选择了,相反,如果没有选择第一组中的第一个物品,那么就要选择,第一组中的其他物品,当然在选择的时候,相当于是01背包,所以在选择物品的时候,要进行判断
j >= v [ k ]
v [ k ] :某一组中的某一个物品的体积

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int n, m, s;
int v[N], w[N];
int f[N];int main()
{cin >> n >> m;for(int i = 0; i < n; i ++ ){int s;cin >> s;for(int j = 0; j < s; j ++ ) cin >> v[j] >> w[j];for(int j = m; j >= 0; j -- )for(int k = 0; k < s; k ++ )if(j >= v[k])f[j] = max(f[j], f[j - v[k]] + w[k]);}cout << f[m] << endl;return 0;
}

7. 背包问题求方案数

特点:在背包问题下,求出最优选法的方案数

题目链接

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示 方案数 模 109+7 的结果。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

2

f [ j ] 表示体积恰好是 j 的情况下,最大价值是多少
g [ j ] 表示体积恰好是 j 的情况下,方案数是多少

这个地方的 f [] 数组的含义与前面的背包问题不一样,所以在这里的初始化,也与前面不同,这个地方在前面的背包问题上有所提到
f [ 0 - N ] = -∞
f [ 0 ] = 0,g [ 0 ] = 1

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;int n, m;
int v[N], w[N];
int f[N], g[N];
int main()
{cin >> n >> m;g[0] = 1;for(int i = 1; i <= m; i ++ ) f[i] = -INF;for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i];for(int i = 0; i < n; i ++ )for(int j = m; j >= v[i]; j -- ){int t = max(f[j], f[j - v[i]] + w[i]);int s = 0;if(t == f[j]) s += g[j];if(t == f[j - v[i]] + w[i]) s += g[j - v[i]];if(s >= mod) s -= mod;f[j] = t;g[j] = s;}int maxx = 0;for(int i = 0; i <= m; i ++ ) maxx = max(maxx, f[i]);int ans = 0;for(int i = 0; i <= m; i ++ )if(maxx == f[i]){ans += g[i];if(ans >= mod) ans -= mod;}cout << ans << endl;return 0;
}

8. 求背包问题的方案

特点:在普通背包的基础下,输出选择方案,比如选择物品1 、 3 、 4是最优解,那么就输出 1 、 3 、 4

题目链接

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。

物品编号范围是 1…N。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

1 4

我们要输出所选的方案数,首先我们按照普通背包的问题求解后,我们会得到 f [] 数组,因此如果我们要求出选择了哪一些物品,我们就可以对 f [] 数组进行反推,
如果 f [ i ][ j ] == f [ i ][ j ] ,说明选择了第 i 个物品
如果 f [ i ][ j ] == f [ i - 1 ][ j - v[ i ] ] ,说明选择了第 i - 1 个物品
这个题目为了解唯一,他的答案方案数是字典序最小的方案,因此我们可以按照贪心的思想去做,如果能选择第 i 个物品,那么我们就直接选第 i 个物品,而不选第 i + 1 个物品(如果选择第 i 个和选择第 i + 1 个物品所得到的最大价值相同的话)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;int n, m;
int v[N], w[N];
int f[N][N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];for(int i = n; i >= 1; i -- )for(int j = 0; j <= m; j ++ ){f[i][j] = f[i + 1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);}int vol = m;for(int i = 1; i <= n; i ++ )if(vol - v[i] >= 0 && f[i][vol] == f[i + 1][vol - v[i]] + w[i]){cout << i << ' ';vol -= v[i];}   return 0;
}

9. 有依赖的背包问题

特点:N个物品,V容量的背包,物品之间有依赖关系,且依赖关系可以构成一棵树,如果选择某一个物品那么就必须先选择这个物品的父亲物品

题目链接

有 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

f [ i ][ j ] 表示选择结点 i (在这里也就是根节点),总体积是 j 时,最大价值是多少

当我们选择根节点后,对于根节点的每一棵子树,都相当于对根节点做分组背包了,每一个根节点相当于一个组,对于组中的节点分为选择或者不能选择

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>using namespace std;
typedef long long ll;
const int N=1010;int n, m;
int v[N], w[N];
int f[N][N];
int h[N], e[N], ne[N], idx;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 != -1; i = ne[i]){int son = e[i];dfs(son);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]);}// 将根节点 u 加上 for(int i = m; i >= v[u]; i -- )f[u][i] = f[u][i - v[u]] + w[u];// 如果选择了子节点后,还没有选择到最后的 root ,那么就不能选择当前结点及其根节点了 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;
}

背包九讲(超详细 :算法分析 + 问题分析 + 代码分析)相关推荐

  1. 算法--背包九讲(详细讲解+代码)

    背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问题  第六讲 分组的背包问题  第七讲 有依赖的背包问题  ...

  2. 【算法】动态规划+“背包九讲”原理超详细讲解+常见dp问题(9种)总结

    目录 一.动态规划(DP) 二.背包九讲 (1)完全背包 P1616 疯狂的采药(完全背包) (2)01背包 滚动数组 一维数组 P1048 采药(01背包) 01背包表格图示 (3)多重背包 整数拆 ...

  3. dalao的背包九讲

    背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问题  第六讲 分组的背包问题  第七讲 有依赖的背包问题  ...

  4. 【转】《背包九讲》--崔添翼大神

    背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 第八讲 泛化物 ...

  5. 转载——背包九讲(原文链接已不可考)

    浙大崔添翼对背包问题的讲解,观点很高也很深刻,特此转载. 背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问 ...

  6. dd大牛的背包九讲-背包问题汇总

    背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问题  第六讲 分组的背包问题  第七讲 有依赖的背包问题  ...

  7. 完全背包问题python_令人头疼的背包九讲(2)完全背包问题

    重磅干货,第一时间送达 背包问题是一个经典的动态规划模型.它既简单形象容易理解,又在某种程度上能够揭示动态规划的本质,故不少教材都把它作为动态规划部分的第一道例题. 题目 完全背包问题 题目要求 有n ...

  8. dp背包九讲(待补充,暂时前两讲)

    文章目录 背包九讲 一.01背包 二维dp 优化为一维 二.完全背包 二维dp 优化为一维 三.多重背包 数据范围很小时,变成01背包暴力 当数据范围较大时,利用二进制优化 二进制优化还不够用时,利用 ...

  9. 第二讲 完全背包问题(对背包九讲的学习)

    学习自:背包九讲 题目 有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. ...

  10. 背包九讲之一:01背包问题

    文章目录 说明: 01背包问题 题目 基本思路 初始化的细节问题 优化空间复杂度 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添 ...

最新文章

  1. hdu5185 dp:和为n且满足后一项是前一项或者+1的数列个数
  2. 交换算法经常使用的两个数的值
  3. 一段简单的打印代码(c#)
  4. 带有.NET Core App的Identity Server 4
  5. mybatis批量删除提示类型错误
  6. thinkphp html里使用c u,原来C可以这么用 Thinkphp C
  7. mysql connections 作用_MySQL性能优化之max_connections配置参数浅析
  8. (原创)二十一天定律
  9. 数据库应用之(教育)
  10. BUUCTF_Crypto题目题解记录1
  11. Leetcode-1737-满足三条件之一需改变的最少字符数
  12. 风险投资(VC)私募股权投资(PE)30问答
  13. Serverless 底座的持续创新
  14. DebEX 现在支持 Linux 5.0 内核 和 Budgie Desktop 10.4
  15. 我家云粒子云刷机教程刷机助手下载
  16. 鸿蒙ArkUI即将取代Java UI?
  17. python导出项目pip_pip导出当前项目所用的包list列表
  18. JS如何判断一个值是否是数值(整数或者浮点数)
  19. failed to install Tomcat6 service解决办法
  20. 百面机器学习—7.K均值算法、EM算法与高斯混合模型要点总结

热门文章

  1. PSINS捷联惯导更新算法
  2. 小白如何上手产品经理
  3. Altium Designer 18简单入门介绍与分享
  4. Iar新建工程配置使用教程
  5. 【蓝桥杯】历年真题题目及题解汇总
  6. 【实用sci论文常用词语】
  7. Mac更新Big Sur或者Monterey后没有管理员账户或当前账户没有管理员权限或rm /var/db/.AppleSetupDone: No such file or directory解决方案
  8. 基于实时流的数据基础-分享
  9. 口罩预约管理系统——数据库设计(前端+PHP+MySQL)
  10. 基于射频的石油热解开采的一些资料