一、 0-1背包问题
1) 问题描述
给定n种物品:物品i的重量是w[i], 物品的价值是v[i];
给定一个背包, 背包最大能装重量为capacity的物品;(物品不可分割)
输入数据:
n = 5;
w[n] = { 0, 2, 2, 6, 5, 4}; //0是占位,可忽略
v[i] = {0, 6, 3, 5, 4, 6};
2) 动态规划与贪心算法的区别:
动态规划时候,做选择时候要考虑子问题的最优解;
贪心算法时候, 只要做出相对于当前局部的最优解就可以, 不考虑子问题的解;
3)问题分析:找出子问题空间和影响子问题两个维度的两个因素
从问题描述中可以知道几个参数:重量、价值、物品编号;价值这个参数肯定是最优解的参数,剩余两个参数(重量, 编号)可虑作为子问题空间的维数
例如,选择物品i放入背包,会导致背包容量变化,所以可以知道背包容量大小的变化是子问题的一部分,设背包容量变化为capacity = {0, 1, 2, …, 10};
子问题空间的另一部分是选择哪一种物品,设悬着物品i = {1, 2, 3, 4, 5};
则0-1背包问题变为求解m(i, capacity)的最优解;即当背包空间为capacity的时候, 选择物品i时候背包的价值最大;
4)确定边界条件:
假设第一个选择就是编号为5的物品放入背包(因为编号5的物品价值最大), 当背包容量>= 4的时候, 物品5可以放入背包;
如下表,在选择物品5放入背包后, 背包的价值:
之后,在此基础上选择物品4, 此时假设已经做过对物品5的选择;
1) capacity < w[4],不能放入物品4, 背包中的价值为之前价值, 即m(4+1, capacity);
2) capacity>=w[4], 可以选择是否放入物品4,
A)如果还是不放入物品4, 则背包中的价值是之前背包的价值, 即m(4+1, capacity);
B)如果放入物品4,则背包的价值=当前物品的价值 + m(4+1, capacity-w[4])价值的和; 仔细理解动态规划算法的特点, 每一个不同的选择会导致不同的子问题(选择会影响子问题的解,贪心算法只是做出选择而不关心子问题的解), 如果选择将物品4放入背包,则背包剩余空间是capacity - w[4], 其子问题的最优解变成m(4+1, capacity - w[4]);——动态规划的每一次选择都影响子问题的解
比较m(4+1, capacity) 和 m(4+1, capacity-w[4]) + v[4]的值,来判断是否选择物品4
因此填表时候,当capacity = 5的时候, 选择不放物品4的背包价值是6, 放物品4的背包价值是m(4+1, capacity-5) + v[4] = 4,因此不放物品4;
一直到capacity = 9的时候m(4+1, capacity-5) + v[4] = 10;此时将物品4放入背包;
(重点:每一次做选择的时候,都要考虑这个选择对子问题的影响);
0-1背包问题的最优子结构:
边界条件是选择价值最大的物品第一个放入背包(下面图片中, capacity就是j:
动态规划的状态方程是,(capacity就是j):
二、代码
使用m[i][j] 表示当背包剩余空间为j的时候,选择物品i, 背包的价值是多少;
#include <stdio.h>
#include <stdlib.h>
int w[] = {
0, 2</