• 1008-----算法笔记----------0-1背包问题(动态规划求解)


    1.问题描述

      给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为C。问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大?

    2.问题分析

      上述问题可以抽象为一个整数规划问题,即求满足 (a)Σwixi ≤ C;(b)xi(0,1),  1≤i≤n;条件下,∑vixi最大时的一个物品xi序列。分析问题可以发现,该问题具有最优子结构性质,那么就可以尝试用动态规划方法求解,而动态规划求解的关键就是列出问题的递归关系表达式。

      设m(i,j)为背包容量为j,可选物品为i,i+1,...n时0-1背包问题的最优质,那么可有如下递归式:

      m(i,j) = { max( m(i+1, j), m(i+1, j-wi)+vi);   j>=wi;

             { m(i+1, j);               j<wi;

    要求的是m(1,c),此时问题就转化为填充m数组的问题了,以n = 5, c = 10, w[] = {2,2,6,5,4},v[] = {6,3,5,4,6},填充的过程如下图所所示,主要是用上述递归式求值,考虑当前物品能否放入,放入当前物品和不放入导致最终的价值哪个大,图中阴影部分为回溯求xi的过程,表示0,1,4号物品被放入背包中。

    i/j 1 2 3 4 5 6 7 8 9 10
    4 0 0 0 6 6 6 6 6 6 6
    3 0 0 0 6 6 6 6 6 10 10
    2 0 0 0 6 6 6 6 6 10 15
    1 0 3 3 6 6 9 9 9 9 9
    0 0 6 6 9 9 12 12 15 15 15

     

    3.源码

      3.1 非递归

    #include <stdio.h>
    
    #define N 1024
    #define max(x, y) (x > y ? x : y)
    #define min(x, y) (x < y ? x : y)
    
    void knapsack(int *w, int *v, int c, int n, int (*m)[N]);
    void traceback(int *w, int (*m)[N], int *x, int n, int c);
    
    
    
    
    int main(int argc, const char *argv[])
    {
        int n, c, w[N], v[N], m[N][N], x[N], i;
        scanf("%d%d", &n, &c);
        for(i = 0; i < n; i++)
            scanf("%d", &w[i]);
        for(i = 0; i < n; i++)
            scanf("%d", &v[i]);
    
        knapsack(w, v, c, n-1, m);      //这里传的是数组的最大下标
        traceback(w, m, x, n-1, c);     //求出是否装载的序列 x[i]
    
        printf("Max v: %d
    ", m[0][c]);
        for(i = 0; i < n; i++)
            printf("x[%d] = %d	", i, x[i]);
        printf("
    ");
    
        return 0;
    }
    
    void knapsack(int *w, int *v, int c, int n, int (*m)[N]){
        int j, jMax, i;
        jMax = min(w[n] - 1, c);
        for(j = 0; j <= jMax; j++)      //求m[n][]
            m[n][j] = 0;
        for(j = c; j > jMax; j--)
            m[n][j] = v[n];
    
        for(i = n-1; i > 0; i--){       //依次求m[n-1][] - m[1]
            jMax = min(w[i]-1, c);
            for(j = 0; j <= jMax; j++){
                m[i][j] = m[i+1][j];
            }
            for(j = c; j > jMax; j--){
                m[i][j] = max(m[i+1][j], m[i+1][j-w[i]] + v[i]);
            }
        }
    
        m[0][c] = m[1][c];              //求m[0][]
        if(w[0] < c){
            m[0][c] = max(m[1][c], m[1][c-w[0]] + v[0]);
        }
    }
    
    void traceback(int *w, int (*m)[N], int *x, int n, int c){
        int i;
        for(i = 0; i < n-1; i++){
            if(m[i][c] == m[i+1][c])    //根据m数组 判断是否装进去
                x[i] = 0;
            else{
                x[i] = 1;
                c -= w[i];
            }
        }
        x[n] = (m[n][c] > 0) ? 1 : 0;
    }

      3.2 递归

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define N 5
    #define C 10
    
    int w[] = {2, 2, 6, 5,4};           //使用递归 避免传递太多参数,不清晰
    int v[] = {6, 3, 5, 4,6};           //因此设置成全局变量
    int m[N][C], x[N];
    
    int knapsack(int i, int j);
    void traceback();
    
    int main(int argc, const char *argv[])
    {
        int i;
        knapsack(0, C);
        traceback();
    
        printf("Max :%d
    ", m[0][10]);
        for(i = 0; i < N; i++)
            printf("x[%d] = %d	", i, x[i]);
        printf("
    ");
    
        return 0;
    }
    
    int knapsack(int i, int j){
        if(i == N-1){
            m[i][j] = (j > w[i] ? v[i] : 0);
            return  m[i][j];
        }
    
        int ret1, ret2;
        if(j < w[i]){
            m[i][j] = knapsack(i+1, j);
        }
        else{
            ret1 = knapsack(i+1, j);
            ret2 = knapsack(i+1, j-w[i]) + v[i];
            m[i][j] =  ret1 > ret2 ? ret1 : ret2;
        }
        return m[i][j];
    
    }
    
    
    void traceback(){
        int i, n = N-1, c = C;
        for(i = 0; i < n; i++){
            if(m[i][c] == m[i+1][c])    //根据m数组 判断是否装进去
                x[i] = 0;
            else{
                x[i] = 1;
                c -= w[i];
            }
        }
        x[n] = (m[n][c] > 0) ? 1 : 0;
    }

      3.3 书上有种改进的算法,采用跳跃点实现,暂时还没看懂,也许过两天在看就懂了呢。

  • 相关阅读:
    MySQL事务知识要点(一)
    MySQL 安全体系知识要点
    MySQL 安全性知识要点
    #翻译#通往SQL Server安全级别2的阶梯:身份验证; 唐•吉利; 原文链接:Stairway to SQL Server Security Level 2: Authentication http://www.sqlservercentral.com/articles/Stairway+Series/109975/
    SQL子查询&视图&更新&删除思维导图-误点难点(附示例代码)
    #翻译#通往t
    香港中文大学-人脸识别进展-2014-06
    NotesMITLinearAlgebra
    Notes Berkerly Statistics 2.1X Week4
    Notes on Brain Study
  • 原文地址:https://www.cnblogs.com/monicalee/p/4012180.html
Copyright © 2020-2023  润新知