本解法的价值矩阵生成由上到下,行表示不同的物品编号,第i行表示有i-1种物品;列表示重量限制。r[i][j]表示仅有i-1号物品时,重量限制为j时,可以得到的最大价值。
价值矩阵r[i][j],i是物品的编号,j是背包的重量限制;物品编号矩阵p[i][j],表示达到价值r[i][j]时,背包里的物品最大编号。可以通过p[i][j] - p[i][ j - w[i - 1]]得到除去该最大编号物品后,物品第二大的编号,以此类推得到全部物品的清单。
r的生成是从上到下的,r[i][j] = max( r[i - 1][j], r[i][j - w[i - 1]] + v[i - 1] )。前一个是不加入节点i,则价值与只有i-1个节点,重量限制为j的价值相同。后一个是加入一个节点i,则价值为本行去除节点i的重量后,重量限制j-w[i]对应的价值,加上节点i能带来的价值v[i-1]。
下方大篇幅为完全背包代码。
若0-1背包有以下区别。
区别1 初始化时:
r[1][i] = i / w[0] * v[0]; ---> if(i<w[0]) r[1][i] = 0; else r[1][i] = v[0];区别2 决定是否加入物品i时:
if(r[i - 1][j] > v[i - 1] + r[i][j - w[i - 1]]) ---> <pre name="code" class="cpp">if(r[i - 1][j] > v[i - 1] + r[i - 1][j - w[i - 1]])
#include <iostream> #include <memory.h> using namespace std; int main(){ int v[] = {1, 3, 5, 9}; int w[] = {2, 3, 4, 7}; int b = 10; int n = sizeof(v) / sizeof(v[0]); int **p = new int* [n + 1];//记录达到当前价值时装入的物品的最大编号,用于确定最后需要如何装入物品来达到最大价值 int **r = new int* [n + 1];//记录动态规划的最大价值 int i, j; for(i = 0; i < n + 1; i++){//创建二维矩阵,并初始化第一列的值为1 r[i] = new int [b + 1]; p[i] = new int [b + 1]; r[i][0] = 0; p[i][0] = 0; } for(i = 0; i < b + 1; i++){//只装入第一件物品,作为动态规划的起始值 r[0][i] = 0; r[1][i] = i / w[0] * v[0]; p[0][i] = 0; if(r[1][i]){//不为0,说明装入了第一件物品 p[1][i] = 1; } else{ p[1][i] = 0; } } for(i = 2; i < n + 1; i++){ for(j = 1; j < b + 1; j++){ if(w[i - 1] > j){//当前物品装不进去 r[i][j] = r[i - 1][j]; p[i][j] = p[i - 1][j]; } else{//能装入当前物品,比较装入该物品(该物品价值 + 去掉该物品重量后,剩余重量所能达到的最大价值)和不装入该物品哪种情况价值更高,选择高的那个 if(r[i - 1][j] > v[i - 1] + r[i][j - w[i - 1]]){//不装入当前物品 r[i][j] = r[i - 1][j]; p[i][j] = p[i - 1][j]; } else{//装入当前物品 r[i][j] = v[i - 1] + r[i][j - w[i - 1]]; p[i][j] = i; } } } } int *o = new int [n];//记录达到最大价值时装入的每一件物品的数量 memset(o, 0, n * sizeof(int)); i = b;//当前背包重量 while(i > 0){ j = p[n][i]; if(j > 0){ o[j - 1]++; i -= w[j - 1]; } else{ break; } } cout<<"最大价值:"<<r[n][b]<<endl; for(i = 0; i < n; i++){ cout<<"物品"<<i + 1<<":"<<o[i]<<"件"<<endl; } return 0; }