01背包
问题的引入: 01背包简化版
现有N个物品,每个物品重量为W,这些物品能否使载重量为S的背包装满(即重量和正好为S)?如果不能正好装满,那么背包最多能装多少重量的物品?
【输入文件】
第一行两个正整数N(0 < N < 1000)和S(0 < S < 10000)表示物品的个数和背包的容量,第二行N个整数列出这N个物品各自的重量。
【输出文件】
单独一行,表示背包最大的载重量。
【输入样例】
6 24
8 3 12 7 9 7
【输出样例】
24
【分析】
针对这一问题我们以物品的个数分阶段,设计一个状态dp[i]表示载重量为i的背包可否装满,显然dp[i]的基类型是bool。
决策是什么呢?
当要装第i个物品时,如果前i-1个物品可以使载重为 k的背包装满,那么载重为k+w[i]的背包就可以装满。于是对于一个dp[j]来说,只要dp[j-w[i]]是true(表示可装满)那dp[j]就可以装满。
但要注意:针对每一个物品枚举背包的载重量时如果这样正向的推导会使同一个物品用好几次,因为k+w[i]可装满那么k+w[i]+w[i]就可装满,但实际上是装不满的,因为物品只有一个。解决这个问题很简单,只要逆向推导就OK了。
这样划分阶段,设计状态,满足无后效性和么?
显然对于每一个物品(阶段)都是独立的,物品的顺序并不影响求解,因为装物品的次序不限。而对于dp[j]只考虑dp[j-w[i]]而不考虑后面的,所以满足无后效性。
有了上面的分析不难写出状态转移方程:
dp[j] = dp[j-w[i]] {dp[j-w[i]] = true}
参考程序:
#include <iostream>
#include <cstdio>
using namespace std;
int w[1005], dp[1005];
int main(){
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%d", &w[i]);
dp[0]=1;
for (int i = 1; i <= n; i ++)
for (int j = m; j >= w[i]; j --)
if (dp[j-w[i]]==1) dp[j] = 1;
for (int i = m; i >=0; i --)
if (dp[i] == 1){ printf("%d\n", i); break; }
return 0;
}
【例题1】 装箱问题
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30=,每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
【输入文件】
第一行一个正整数V表示箱子的容量,第二行一个正整数N表示物品个数,接下来N行列出这N个物品各自的体积。
【输出文件】
单独一行,表示箱子最小的剩余空间。
【输入样例】
24
6
8
3
12
7
9
7
【输出样例】
0
【问题分析】
本题是经典的0/1背包问题,并且是0/1背包的简化版,把箱子看做背包,容量看做载重量,体积看做重量,剩余空间最小也就是尽量装满背包。于是这个问题便成了:
有一个栽重量为V的背包,有N个物品,尽量多装物品,使背包尽量的重。
设计一个状态opt[i]表示重量i可否构成。
状态转移方程:opt[j]:=opt[j-w[1]] {opt[j-w[i]]=true}
最终的解就是v-x(x<=n 且opt[x]=true 且 opt[x+1..n]=false)。
参考程序:
#include <iostream>
#include <cstdio>
using namespace std;
int w[1005], dp[1005];
int main(){
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%d", &w[i]);
dp[0]=1;
for (int i = 1; i <= n; i ++)
for (int j = m; j >= w[i]; j --)
if (dp[j-w[i]]==1) dp[j] = 1;
for (int i = m; i >=0; i --)
if (dp[i] == 1){ printf("%d\n", m-i); break; }
return 0;
}
【例题2】 砝码称重
设有1g、2g、3g、5g、10g、20g的砝码各若干枚(其总重<=1000),用他们能称出的重量的种类数。
【输入文件】
a1 a2 a3 a4 a5 a6
(表示1g砝码有a1个,2g砝码有a2个,…,20g砝码有a6个,中间有空格)。
【输出文件】
N
(N表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的