在说01背包问题前,简单的说下部分背包的问题。
部分背包的问题,有N个可分割的物品,每个物品对应重量Wi, 价值Vi(i从1到N)。给定总重量C,如何保证最大价值?
01背包问题,和部分背包问题唯一不同的是,01背包问题要求物品i,要么全部拿走,要么不拿走。而对应的部分背包问题,则可以拿一小部分物品i,再拿另一小部分物品i+1,以此类推。
针对部分背包问题,直接贪心算法解决就行了,思路是求出每个物品的单位价值Vi/Wi,然后排序,按照单位价值最高的开始拿,直到当前物品拿完或者达到给定的总重量,这个实现起来没什么难度,这里主要讲01背包问题。
如果你对动态规划理解很到位,那么这个问题还是挺简单的。按照惯例,我们给出一个辅助二位数组b,其中行标是物品标号i,列标是给定的总重量Ci。我们只需要通过构造每个Ci下,对应的最大价值b[i][j]就行了。而这个b[i][j]和其他的动态规划一样,无非来自两个地方,b[i-1][j]或b[i-1][j-wi]+Vi。如果不懂,还是百度原理吧,这里给出代码。
max_taken函数,构造数组b,数组b内容含义是在给定总重量j的情况下,能在i个物品中,拿到的最大价值!!!
int max_taken(int *v, int *w, int n, int Capacity, int ***b) { if (n < 0 || Capacity < 0) return -1; int ret, i, j; *b = new int *[n + 1]; for (i = 0; i <= n; i++) (*b)[i] = new int[Capacity + 1]; //初始化 for (i = 0; i <= n; i++) for (j = 0; j <= Capacity; j++) (*b)[i][j] = 0; for (i = 1; i <= n; i++) for (j = 1; j <= Capacity; j++) if (j >= w[i - 1]) (*b)[i][j] = max((*b)[i - 1][j], (*b)[i - 1][j - w[i - 1]] + v[i - 1]);//假设拿物品i-1 else (*b)[i][j] = (*b)[i - 1][j];//不拿 ret = (*b)[n][Capacity]; /* for (i = 0; i <= n; i++) delete[](*b)[i]; delete[](*b); */ return ret; }
which_taken函数,对数组b进行解释,从而知道组成这个最大价值的物品有哪些?说实话,就是对构造b数组的一个逆向求解。
void which_taken(int **b, int *w, int n, int Capacity) { int i = n, j = Capacity; while (j) {//始终保持j大于零 if (b[i][j] != b[i - 1][j]) {//满足条件 printf("%02d ", i - 1);//数组下标 j -= w[i - 1];//需要调整j } i--; } printf(" "); }
数据录入
int v[] = { 60, 100, 120 }, w[] = { 10,20,30 };//这是《算法导论》上的数据
Main函数 (老规矩,我还是把物品标号和数组下标进行了改变)
#define LENGTH(x) sizeof(x)/sizeof(x[0]) #define max(a,b) ((a)>(b))?(a):(b) //#define CAPACITY 12 #define CAPACITY 50
int main() { //int v[] = {10, 2, 8, 3, 7, 6}, w[] = { 6, 1, 4, 2, 5, 2}; int v[] = { 60, 100, 120 }, w[] = { 10,20,30 }; int **b; printf("max taken:%d ", max_taken(v, w, LENGTH(v), CAPACITY, &b)); show(b, LENGTH(v) + 1, CAPACITY + 1); //只需要解析CAPACITY对应的列数据 printf("对应数组下标 "); which_taken(b, w, LENGTH(v), CAPACITY); return 0; }
辅助函数
void show(int **b, int m, int n) { for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) printf("%-3d ", b[i][j]); printf(" "); } }
结果图
对应书上的结果示意图
所有代码均经过测试,结果正确!!