一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情。
概念
- 有N个物品,容量是V的背包,每个物品有两个属性,体积Vi,价值Wi
- 每件物品可以有无限个,只要背包装的下,就可以无限装
- 求:在背包能装的下的情况下的最大价值是多少
题目
分析
- 状态计算
- 按照第i个物品选的个数来进行划分
- 第i个物品不选f[i - 1, j]
- 第i个物品选k个
- 曲线救国分析法:
- 每个选法里面去掉k个第i个物品
- f[i - 1, j - k * v[i]] + k*w[i]
- 曲线救国分析法:
- 最终得到的状态转移方程
f[i, j] = f[i - 1, j - v[i] * k] + w[i]*k
- 按照第i个物品选的个数来进行划分
代码
朴素做法
#include <iostream>
#inlclude <algorithm>
using namespace std;
const int N = 1010;
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 = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k * v[i] <= j; k++)
f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k)
cout << f[n][m] << endl;
reutrn 0;
}
优化,将三维优化成两维
f[i, j]可以表示成,不包含第i个物品:f[i - 1, j]跟包含k个第i个物品:f[i - 1, j - v] + w , f[i - 1, j - 2v] + 2w...,他们这几者之间的MAXf[i, j - v]这个状态的表示方法- 不包含第i个物品:
f[i - 1, j - v] - 包含一个第i个物品:
f[i - 1, j - 2v] + w - 包含两个第i个物品:
f[i - 1, j - 3v] + 2w - 包含k个第i个物品:
f[i - 1, j - (k + 1)v] + kw - 在上面这些里面取MAX即可
- 不包含第i个物品:
- 发现: f[i , j ]除了第一项之外,后面的项比f[i , j - v]多w
- 进行替换:
f[i, j] = Max(f[i-1, j], f[i, j - v] + w);->将三维成功化成了一维
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
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 = 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][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
01背包跟完全背包的不同点
01背包是:
f[i - 1][j - v] + w
完全背包是:f[i - 1][j - v] + w
完全背包问题优化成一维
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
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] << endl;
return 0;
}