01背包问题
经典动态规划题了。其实也没有多难。。。
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i i i 件物品的体积是 v i v_i vi,价值是 w i w_i wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
我们可以将问题拆分成两种情况来分析
- 在装到第 i i i 个物品时,选择不装(其中就有背包剩余体积小于物品体积的情况),则 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i−1][j]
- 在装到第 i i i 个物品时,选择装(此时特判一下背包体积是否足够),则 f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − v [ i ] ] + w [ i ] ) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]) f[i][j]=max(f[i][j],f[i−1][j−v[i]]+w[i])
这样问题就得以解决,也是最简单,最直接的做法,也被称为朴素解法。
//01背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXNUM 1010
using namespace std;
int f[MAXNUM][MAXNUM]; //dp表
int v[MAXNUM], w[MAXNUM]; //体积v与价值w
int N,V; //物品数量与背包体积
int main()
{
cin >> N >> V;
for (int i = 1; i <= N; i++)
cin >> v[i] >> w[i];
for (int i=1; i<=N; i++)
for (int j = 1; j <= V; 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 = 1; i <= V; i++) ans = max(ans, f[N][i]);
cout << ans << endl;
return 0;
}
那么在这种方法的背景之下,我们可以对改算法进行改进。
我们看到上述算法,动态规划数组是一个二维数组,对于空间的占用较大,所以将其改进成一维数组 (压榨,不断压榨,榨干它)
在二维数组中
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
v
[
i
]
]
+
w
[
i
]
,
f
[
i
]
[
j
]
)
f[i][j]=max(f[i-1][j-v[i]]+w[i],f[i][j])
f[i][j]=max(f[i−1][j−v[i]]+w[i],f[i][j]),发现第
i
i
i 个只与第
i
−
1
i-1
i−1 个有关,所以,想办法将
i
i
i 消除。
但要保证,当放入第
i
i
i 个物品时,之前的标志仍是
i
−
1
i-1
i−1
这里就将 j j j 反向遍历,从最后一位向前遍历,知道 j < v [ i ] j<v[i] j<v[i] 为止。这样,最后的 f [ v ] f[v] f[v] 自然就是最大值。
举个例子:
如 第 k 个是最大值,那么第 v 个最终会从第 v-k 个状态转移过来,依次类推,最终是会从 第 1 个状态转移过来。而初始化时所有的状态都是0,故最终留在第 v 个状态的必定时最大值。
// 01背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXNUM 1010
using namespace std;
int f[MAXNUM]; //dp表
int v[MAXNUM], w[MAXNUM]; //体积v与价值w
int N,V; //物品数量与背包体积
int main()
{
cin >> N >> V;
for (int i = 1; i <= N; i++)
cin >> v[i] >> w[i];
for (int i=1; i<=N; i++)
for (int j = V; j >=v[i] ; j--)
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
cout << f[V] << endl;
return 0;
}
大概就是这样。。。