回 帖 发 新 帖 刷新版面

主题:动态规划解背包问题

题目如下:
一个容量为capacity的背包,有n个物品,每个物品都有其价值和重量,把物品放入背包中,只要重量和少于capacity就可以,要求使背包中物品的价值总和最大,求这个最大值( 重量和价值都是正整数)

可以分别考虑对重量和价值进行动态规划处理:
#include <stdio.h>
#include <stdlib.h>
#define FINITE ( 0x3fffffff )

typedef int Weight;

/* 对价值进行处理的方法 */

int Knapsack( const Weight weight[],       // 重量数组
              const int value[],           // 价值数组
              int n,                       // 物品个数
              Weight capacity )            // 背包容量
{
    int i, j, k, max;

    for ( i = 0, max = 0; i < n; ++i )
    {
        max += value[i];
    }
    Weight* present = ( Weight* )malloc( ( max + 1 ) * sizeof( Weight ) );
    Weight* previous = ( Weight* )malloc( ( max + 1 ) * sizeof( Weight ) );
    Weight* tmp;

    for ( i = 0; i <= max; ++i )
    {
        present[i] = ( i <= value[0] ? weight[0] : FINITE );
    }
    for ( i = 1; i < n; ++i )
    {
        tmp = present;
        present = previous;
        previous = tmp;
        for ( j = max; j >= 0; --j )
        {
            present[j] = previous[j];
            k = j + value[i];
            if ( k <= max && previous[j] + weight[i] < present[k] )
            {
                present[k] = previous[j] + weight[i];
            }
        }
    }

    for ( i = max; i > 0 ; --i )
    {
        if ( present[i] <= capacity )
        {
            break;
        }
    }
    free( present );
    free( previous );
    return i;
}

typedef int Value;

/*对重量处理的方法*/

Value Knapsack2( const int weight[],     // 重量数组
                 const Value value[],    // 价值数组
                 int n,                  // 物品个数
                 int capacity            // 背包容量
                )
{
        Value* previous = ( Value* )malloc( ( capacity + 1 ) * sizeof( Value ) );
        Value* present  = ( Value* )malloc( ( capacity + 1 ) * sizeof( Value ) );
        Value* temp = NULL;
        Value max_value;
        int i, j, k;

        for ( j = 0; j <= capacity; ++j )
        {
                previous[j] = ( j < weight[0] ? 0 : value[0] );
        }
        for ( i = 1; i < n; ++i )
        {
                for ( j = 0; j <= capacity; ++j )
                {
                        present[j] = previous[j];
                        k = j - weight[i];
                        if ( k >= 0 && previous[k] + value[i] > present[j] )
                        {
                                present[j] = previous[k] + value[i];
                        }
                }
                temp = present;
                present = previous;
                previous = temp;
        }
        max_value = previous[capacity];
        free( previous );
        free( present  );
        return max_value;
}

int main( void )
{
        Weight weight[] = { 100, 14, 10 };
        Value value[] = { 20, 18, 19 };

        printf( "%d\n", Knapsack( weight, value, 3, 116 ) );
        system( "pause" );
        return 0;
}

回复列表 (共5个回复)

沙发

呵呵 过来补充一下
先说一下第二个的状态和转移方程

f[i,j]表示前i个物品在容量j情况下的最大可得价值

有两种决策从i-1到i
1.舍第i个 f[i,j] = f[i-1,j]
2.取第i个 f[i,j] = f[i-1,j-w[i]]+v[i]
其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值

那么转移方程就为
j>=w[i]时,f[i,j] = max( f[i,j] = f[i-1,j], f[i-1,j-w[i]]+v[i] )
j< w[i]时,f[i,j] = f[i-1,j]

写的时候我采用了一个节省空间的方法
就是因为f的第一维i总是只需要前一个,所以这一维的长度可以限定在2
那么我用两个1维向量来存储状态,空间复杂度由O(N*M)降到O(N)

板凳

第一个解法的思想类似 只不过转移量由重量换成了价值

f[i,j] 表示前i个物品取得价值j所组合出的最小的重量

有两种决策从i-1到i
1.舍第i个 f[i,j] = f[i-1,j]
2.取第i个 f[i,j] = f[i-1,j-v[i]]+w[i]
其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值

那么转移方程就为
j>=v[i]时,f[i,j] = min( f[i,j] = f[i-1,j], f[i-1,j-v[i]]+w[i] )
j< v[i]时,f[i,j] = f[i-1,j]

最后的结果就是j从最大值向0开始遍历f[n,j],第一个满足f < capacity 的值即为答案

3 楼

楼主的有点复杂!!!
BPTTC兄的不错....

4 楼

不过,降维时可以用"滚动数组"。。。。。。

5 楼

[quote]楼主的有点复杂!!!
BPTTC兄的不错....[/quote]

....

lz发的第二个程序是就我写的

滚动数组和两个一维向量的思想一致,可以看作这一维的长度为2的滚动数组

我来回复

您尚未登录,请登录后再回复。点此登录或注册