基本步骤:
1、定义原问题和子问题
原问题:将m件物品放入容量为W的背包中所能获得的最大利润。
子问题:将前 i(i<=m)件物品选择性的放入容量为 j(j<=W)的背包中所能获得的最大利润。
2、定义状态函数
定义状态函数 f(i,j) 表示把i件物品放入 j 大小的背包中所能获得的最大利润。
则 f(m,W)就是我们要求的结果。
因为有两个变量,所以DP数组为二维数组。
3、寻找状态转移方程
特殊情况(边界条件):
i 等于1,只要这一件物品重量(w1)小于背包容量 j ,那么就可以获得这一件物品的利润,即 f(1,j)=v1(第1件物品的利润),否则等于0。
j等于1,我们只能选择重量为1的物品放入背包,即 f(i,1)=vi,否则为0。
一般情况:
我们已经安排好了前 i-1 件物品的存放,现在第 i 件物品摆在我们面前。我们只有放和不放两种选择。
选择不放(第i件物品甚至大于背包容量),那么问题很简单,f(i,j)=f(i-1,j)。
选择放(第i 件物品小于等于背包容量),那么又会出现两种情况。
等于:放了第 i 件物品,前面的i-1件物品就塞不下了,那么利润就是vi.
小于:放了第 i 件物品,前面的还能塞一点,那么我们是不是可以把问题转化为在 i-1 件物品中挑一些放入容量为 j-wi 的背包中?即此时的利润为 f(i-1,j-wi)。
4、matlab编程求解(输出选择的物品编号)
%来自清风老师
function [f, IND] = knapsack01problem2(p,w,W)
% 输入: p:物品的利润 w:物品的重量 W:背包的容量
% 为了编程方便,假设W是大于等于2的正整数;w中每个元素都是大于等于1的正整数
m = length(p); % 物品个数
FF = zeros(m,W); % 初始化DP数组
% FF(i,j):前i件物品选择性的放入容量为j的背包中所能获得的最大利润
if w(1) <= W % 初始化第一行
FF(1,w(1):end) = p(1);
end
for i = 2:m % 初始化第一列
FF(i,1) = max([p(w(1:i) == 1),0]);
end
% i,j>1的情况
for i = 2:m
for j = 2:W
if w(i) > j % 第i件物品的重量w(i)比背包的容量j还要大
FF(i,j) = FF(i-1,j) ;
elseif w(i) == j % 第i件物品的重量w(i)等于背包的容量j
FF(i,j) = max(FF(i-1,j), p(i)); % 不放进去和放进去取较大的值
else % 第i件物品的重量w(i)小于背包的容量j
FF(i,j) = max(FF(i-1,j), p(i)+FF(i-1,j-w(i))); % 不放进去和放进去取较大的值
end
end
end
f = FF(m,W);
IND = []; % 选择的物品编号IND初始化为空
if f > 0 % 只要有利润,就可以利用FF来计算选择的物品编号IND
ww = W; % 初始化背包的剩余容量为整个背包的容量W
tmp = FF(:,ww); % 取出最后一列
while 1 % 不断循环下去,后面通过条件判断来退出循环
ind = find(tmp == max(tmp),1) ; % 找到装入背包的那个物品
ww = ww - w(ind); % 更新背包的剩余容量
IND = [IND,ind]; % 更新IND里面的元素
if ind > 1 && ww>0 % 只要不是第一个物品或者背包容量为空
tmp = FF(1:ind-1,ww); % 重新取出剩余容量的那一列(只保留前面的物品)
else
break % 跳出循环
end
end
IND = sort(IND); % 排序下,输出好看点
end
end