背包问题答题模板
0-1背包
每件商品只能拿一件或者不拿。
1
2
3
4
5
6
| // 假设
int m; // 背包容量
int n; // 商品数量
int v[n]; // 商品的价值列表
int w[n]; // 商品的重量(cost)列表
int dp[n][m];
|
朴素二维数组方式
1
2
3
4
5
6
7
8
| for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++) {
if (j>=v[i])
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
dp[i][j] = dp[i-1][j];
}
}
return dp[m]; // dp数组的最后一个元素即为最优解。
|
滚动数组方式优化
1
2
3
4
5
| for (int i=1; i<=n; i++) {
for (int j=m; j>=w[i]; j--) {
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}
|
一个AC的例程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include <iostream>
using namespace std;
int v[1005];
int w[1005];
int dp[1005];
int main()
{
int n,m;
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--) {
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
}
}
cout<<dp[m]<<endl;
return 0;
}
|
关于dp数组的初始化
要求恰好装满背包时,初始时dp[0]=0
,其他的dp[1...m]=负无穷
。这样就可以保证最终得到的dp[m]
是一种恰好装满背包的最优解。
如果没有恰好装满背包的要求而只是希望价格尽量大时,初始化时应该将dp[0...m]=0
。
初始化时dp数组就是在没有任何物品放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可以在什么都不装且价值为0的情况下被"恰好装满",其他容量的背包均没有合法的解,属于未定义的状态,应该被赋值为负无穷。
如果背包并非必须装满,那么任何容量的背包都有一个合法解”什么都不装“,这个解的价值为0,所以初始时状态的值也就全部为0.
完全背包
每件商品可以拿无数件。
滚动数组
1
2
3
4
5
6
| for (int i=1; i<=n; i++) {
for (int j=m; j>=w[i]; j--) {
for (int k=0; k*w[i]<=j; k++)
dp[j] = max(dp[j], dp[j-k*w[i]]+k*v[i]);
}
}
|
优化
1
2
3
4
5
| for (int i=1; i<=n; i++) {
for (int j=w[i]; j<=m; j++) { // 放一件之后 还能放下 就继续放
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}
|
一个AC的例程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include <iostream>
using namespace std;
int dp[1005];
int main()
{
int n,m;
cin>>n>>m;
int v[1005];
int w[1005];
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++) {
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
}
}
cout<<dp[m]<<endl;
return 0;
}
|
多重背包
每件物品只能拿固定次。
朴素做法
1
2
3
4
5
6
7
| for (int i=1; i<=n; i++) {
for (int j=m; j>=0; j--) {
for (int k=1; k<=s[i] && k*w[i]<=j; k++) { // s[i] 指第i件商品一共有多少件
dp[j] = max(dp[j], dp[j-k*w[i]]+k*v[i]);
}
}
}
|
一个AC的例程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include <iostream>
using namespace std;
int dp[1005];
int main()
{
int n,m;
cin>>n>>m;
int v[1005];
int w[1005];
int c[1005];
for (int i=1; i<=n; i++) {
cin>>v[i]>>w[i]>>c[i];
}
for (int i=1; i<=n; i++) {
for (int j=m; j>=v[i]; j--) {
for (int k=1; k<=c[i] && k*v[i]<=j; k++) {
dp[j] = max(dp[j], dp[j-v[i]*k]+w[i]*k);
}
}
}
cout<<dp[m]<<endl;
return 0;
}
|
二进制优化
另一种优化
这两种优化方式暂时没弄懂
混合背包
二维费用的背包问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| int n; // 商品数目
int v; // 约束1
int m; // 约束2
int c_v[n]; // 约束1的cost
int c_m[n]; // 约束2的cost
for (int i=1; i<=n; i++) {
for (int j=v; j>=c_v[i]; j--) {
for (int k=m; k>=c_m[i]; k--) {
f[j][k] = max(f[j][k], f[j-c_v[i]][l-v_m[i]]+c);
}
}
}
|
下边是一道题的AC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| #include <iostream>
using namespace std;
int v_v[1005];
int v_m[1005];
int w[1005];
int dp[1005][1005];
int main()
{
int n, v, m;
cin>>n>>v>>m;
for (int i=1; i<=n; i++) {
cin>>v_v[i]>>v_m[i]>>w[i];
}
for (int i=1; i<=n; i++) {
for (int j=v; j>=v_v[i]; j--) {
for (int k=m; k>=v_m[i]; k--) {
dp[j][k] = max(dp[j][k], dp[j-v_v[i]][k-v_m[i]]+w[i]);
}
}
}
cout<<dp[v][m]<<endl;
return 0;
}
|