0-1背包问题之动态规划解决算法(一)

问题描述: 
有N件物品和一个容量为c的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。之所以称为”0-1背包问题”表示该问题只有两种结果:装或者不装即0或者1. 

用动态规划算法解决0-1背包问题需要了解以下基本概念和原理:

1.使用动态规划算法必须具备两个基本要素:最优子结构性质和重叠子问题性质
2.动态规划算法常以自底向上的方式计算最优值,也就是说,从最小的子问题开始向包含该子问题的大问题方向求解,把每一次求解出的子问题的解保存下来,以便提供给包含该小问题的大问题使用,因此使用循环迭代方式计算更为合理,但从动态规划算法的两个基本要素可以看出,直接以递归方式计算更为简便,于是乎,就产生了动态规划算法的变形方法:备忘录算法。备忘录算法在递归调用过程中将已经求出的子问题的解保存下来,以便下次递归调用遇到相同的子问题可直接获取该子问题的解。
3.动态规划算法在求解最优值的过程中,将遇到的所有子问题的解记录在一张表中,同时也将一些能构造最优解的必要信息记录下来,以便用这些信息构造最优解。求解最优值的过程也俗称“填表过程”。
4.设计动态规划算法的通常的步骤:
(1)找出最优解的性质,并刻画其结构特征
(2)递归地定义最优值
(3)以自底向上的方式计算最优值
(4)根据计算最优值时得到的信息构造最优解
5.0-1背包问题具有最优子结构性质和重叠子问题性质。
定义m(i,j):从物品i、i+1、...、n-1、n中选择物品装入容量大小为j的背包,使该背包的总价
值最大,最大值为m(i,j),也即最优值。
假设背包容量为c,物品总数n,物品i的重量表示wi,价值表示vi,于是0-1背包问题的最优值为
m(1,c)。计算m(1,c)的值之前,先递归地定义最优值的计算方法:
(1)当背包剩余容量为j时,且j >= wi, m(i,j) = max{ m(i+1,j), m(i+1,j-wi)+vi)};说明
此时的背包剩余容量大于物品i的重量,于是在选取物品i和舍弃物品i之中选择较大的。
(2)当背包剩余容量为j时,且0< j < wi, m(i,j) = m(i+1,j); 说明物品i的重量大于背包此时
剩余容量,于是,对物品i舍弃。
(3)最小的子问题,也即只有一个物品时,如何装入背包使背包的总价值最大。假设背包剩余
容量为j时,同时只有一个物品n供选择时,此时,若j > wn, 则最大价值为wn,即 m(n,j) = 
wn;若j < wn, 则最大价值为0,即 m(n,j) = 0。

注:上述考虑的物品重量和背包容量都为整数。因此,使用二维数组m[i][j]表示m(i,j)

#include "stdio.h"
  
#define C  10  //背包容量数 
#define N  5  //物品总数
  
void Knapsack(int *w, int *v, int n, int c, int (*m)[C+1])  
{  
    //先处理记录表格中的最后一行,也即完成对m[n][j]的“填空” 
	int j,i; 
    int lowc = w[n]-1 > c ? c : w[n]-1;  
    for (j = 0; j <= lowc; j++)  
        m[n][j] = 0;  
    for (j = lowc+1; j <= c; j++)  /*lowc+1等价于我w[n]*/
        m[n][j] = v[n];  
    //循环处理记录表格中的其他行,也即完成对m[i][j]的“填空”  
    for ( i = n-1; i > 1; i--)  
    {  
        lowc = w[i]-1 > c ? c : w[i]-1;  
        for ( j = 0; j <=lowc; j++)  
            m[i][j] = m[i+1][j];  
        for ( j = w[i]; j <= c; j++)  
        {  
            int t1 = m[i+1][j];  
            int t2 = m[i+1][j - w[i]] + v[i];  
            m[i][j] = t1 > t2 ? t1 : t2;  
        }  
    }  
    //处理记录表格中的第一行,也即完成对m[1][c]的“填空”  
    m[1][c] = m[2][c]; //背包容量c不足,物品1舍去  
    if(c >= w[1] && m[2][c-w[1]] + v[1] > m[2][c]) //背包容量c足够,并且选取物品1的情况价值较大  
        m[1][c] = m[2][c-w[1]] +v[1];  
}  
  
void Traceback(int (*m)[C+1],int *w, int *x,int n, int c)  
{  
	int i;
    for (i = 1; i < n; i++)  
    {  
        if(m[i][c] == m[i+1][c])  
            x[i] = 0;  
        else  
        {  
            x[i] = 1;  
            c -= w[i];  
        }  
    }  
    //w[n] < c ? x[n] = 1 : x[n]=0;  
    x[n] = (m[n][c])?1:0;
}  
  
int main(void)  
{  
	int i ;
    int x[N+1];  
    int w[N+1] ={-1,2,2,6,5,4};  //各物品的质量 
    int v[N+1] ={-1,6,3,5,4,6};  //各物品的价值 
    int m[N+1][C+1];  
    Knapsack(w,v,N,C,m); 
	printf("%d\n",m[1][C]); 
    Traceback(m, w, x, N, C);  
    for (i= 1; i <= N; i++)  
    {  
       printf("%d ",x[i]);
    }  
    printf("\n");
    return 0;  
}  

猜你喜欢

转载自blog.csdn.net/qq_40685275/article/details/79964388
今日推荐