Featured image of post 0/1 背包 - NOIP2005P3

0/1 背包 - NOIP2005P3

本文介绍了使用动态规划解决经典的 0/1 背包问题,提供了二维数组和一维数组两种解法,并附上常数优化及NOIP2005P3题目的完整代码。

题目是经典的采药问题。也是最基础的 0/1 背包问题。

我们约定有$N$件物品和一个容量为$C$的背包。第$i$件物品的重量是$w\left [ i \right ]$,价值是$v\left [ i \right ]$。这样,我们所需要求解将哪些物品装入背包可使价值总和最大。

二维数组表示

定义状态:$f\left [ i \right ]\left [ c \right ]$表示前$i$件物品恰放入一个容量为$c$的背包可以获得的最大价值。

状态转移方程:$f\left [ i \right ]\left [ c \right ]=\max\left \{ f\left [ i-1 \right ]\left [ c \right ],f\left [i-1  \right ]\left [ c-w\left [ i \right ] \right ] +v\left [ i \right ]\right \}$

代码模版

1
2
3
4
5
6
7
8
9
for(int i = 1; i <= N; i++)
{
	for(int c = 0; c <= C; c++)
	{
		f[i][c] = f[i - 1][c];
		if(c >= w[i])
		{ f[i][c] = max(f[i][c], f[i - 1][c - w[i]] + v[i]); }
	}
}

时间复杂度、空间复杂度:$O\left ( NC \right )$

一维数组表示

定义状态:由于$i$基本没有什么用处,所以我们把它省略。

状态转移方程:$f\left [ c \right ]=\max\left \{ f\left [ c \right ],f\left [ c-w\left [ i \right ] \right ] +v\left [ i \right ]\right \}$需要注意的是,这时候,我们需要将$c$从$C$开始,倒着推。

代码模版

1
2
3
4
5
6
7
8
for(int i = 1; i <= N; i++)
{
	for(int c = C; C >= 0; C--)
	{
		if(c >= w[i])
		{ f[c] = max(f[c], f[c - w[i]] + v[i]); }
	}
}

时间复杂度:$O\left ( NC \right )$

空间复杂度:$O\left ( C \right )$

一维数组表示下的常数优化

内层循环的下限不需要为 0。

代码模版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int bound, sumw = 0;
for(int i = 1; i <= N; i++)
{
	sumw += w[i];
	bound = max(C - sumw, w[i]);
	for(int c = C; C >= bound; C--)
	{
		if(c >= w[i])
		{ f[c] = max(f[c], f[c - w[i]] + v[i]); }
	}
}

初始化的细节

  • 若要求“恰好装满”:$f\left [ 0 \right ]=0$,其他$f\left [ i \right ]=\textrm{-INF}$。
  • 若不用“恰好装满”:$f\left [ 0 \right ]=0$。

最后附上 NOIP2005P3 的代码:

 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
#include <iostream>
#include <memory.h>
#include <algorithm>

using namespace std;

const int MAX = 1024;

int C, N;
int pC[MAX], pW[MAX], f[MAX];

int main()
{
	cin >> C >> N;
	for(int i = 0; i < N; i++)
	{ cin >> pC[i] >> pW[i]; }
	memset(f, 0, sizeof(f));
	for(int i = 0; i < N; i++)
	{
		for(int j = C; j >= pC[i]; j--)
		{
			f[j] = max(f[j], f[j - pC[i]] + pW[i]);
		} 
	}
	cout << f[C] << endl;
	return 0;
}
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计