动态规划之01背包问题

在说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("
");
    }
}

结果图

对应书上的结果示意图

所有代码均经过测试,结果正确!!

原文地址:https://www.cnblogs.com/dalgleish/p/9183642.html