• 动态规划之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("
    ");
        }
    }

    结果图

    对应书上的结果示意图

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

  • 相关阅读:
    [题解]luogu_P4198_楼房重建(线段树logn合并
    [题解]luogu_P3084(单调队列dp
    [题解]luogu_P3084(差分约束/梦想spfa
    [题解/模板]POJ_1201(差分约束
    [题解]luogu_P5059(矩乘
    [题解]luogu_P5004跳房子2(矩乘递推
    CF1042A Benches(二分答案)
    8.24考试总结
    NOIP2017题目
    「LG3045」「USACO12FEB」牛券Cow Coupons
  • 原文地址:https://www.cnblogs.com/dalgleish/p/9183642.html
Copyright © 2020-2023  润新知