动态规划(三)——如何巧妙解决“双十一”购物时的凑单问题?

1 题目描述

  淘宝的“双十一”购物节有各种促销活动,比如“满 200 元减 50 元”。假设你女朋友的购物车中有 n 个(n>100)想买的商品,她希望从里面选几个,在凑够满减条件的前提下,让选出来的商品价格总和最大程度地接近满减条件(200 元),这样就可以极大限度地“薅羊毛”。作为程序员的你,能不能编个代码来帮她搞定呢?

2 输入

  第一行是物品的个数n(1≤n≤100000),满减大小w(1≤w≤1000000);

  第二行是n个物品的价值。

3 输出

  输出最小值

4 样例输入

6 200
34, 23, 81, 74, 57, 65

5 样例输出

203

6 求解思路

  实际上,它跟第一个例子中讲的 0-1 背包问题很像,只不过是把“重量”换成了“价格”而已。购物车中有 n 个商品。我们针对每个商品都决策是否购买。每次决策之后,对应不同的状态集合。我们还是用一个二维数组 states(n)(x),来记录每次决策之后所有可达的状态。

  不过,这里的 x 值是多少呢?0-1 背包问题中,我们找的是小于等于 w 的最大值,x 就是背包的最大承载重量 w+1。对于这个问题来说,我们要找的是大于等于 200(满减条件)的值中最小的,所以就不能设置为 200 加 1 了。就这个实际的问题而言,如果要购买的物品的总价格超过 200 太多,比如 1000,那这个羊毛“薅”得就没有太大意义了。所以,我们可以限定 x 值为 1001。

  同样的,这个题目也可以使用回溯法与动态规划两种方法解决。

  这里特别强调一下当使用一个数组时,即代码中的第 53 行,j 需要从大到小来处理。如果我们按照 j 从小到大处理的话,会出现 for 循环重复计算的问题,并且还会导致计算出错。
  如果从小到大处理: stat[j + weight[i]] 的赋值为 ture,那么当 j++ 遍历到 j + weight[i] 这个值。就为 ture ,重复计算了。从大到小处理,较大的 stat[j + weight[i]] 的值,影响不到 j-- 后面的。

7 动态规划C++版本代码如下

#include <iostream>
#include <math.h>
#include <string.h>
using namespace std;

#define MAXNUM 100010
#define DRIFT 1001

// items商品价格,n商品个数, w表示满减条件,比如200
int girlLove(int items[], int n, int w) {
    bool states[n][w + 10];
    memset(states, false, sizeof(states));
    // 第一行的数据要特殊处理
    states[0][0] = true;
    if (items[0] <= w + 10) {
        states[0][items[0]] = true;
    }
    for (int i = 1; i < n; ++i) { // 动态规划
        // 不购买第i个商品
        for (int j = 0; j <= w + 10; ++j)
            if (states[i-1][j])
                states[i][j] = states[i-1][j];
        for (int j = 0; j <= w + 10-items[i]; ++j)
            //购买第i个商品
            if (states[i-1][j]){
                //cout<<j+items[i]<<endl;
                states[i][j+items[i]] = true;
            }
    }
    for(int i = 0; i <= w; i++)
        cout<<states[n - 1][i]<<" ";
    cout<<endl;
    int j;
    for (j = w; j < w + 10; ++j) {
        // 输出结果大于等于w的最小值
        if (states[n-1][j] == true)
            return j;
    }
    // 没有可行解
    if (j == w + 10 +1)
        return -1;
}

int girlLovePlus(int items[], int n, int w) {
    bool states[w + 10];
    memset(states, false, sizeof(states));
    // 第一行的数据要特殊处理
    states[0] = true;
    if (items[0] <= 3 * w)
        states[items[0]] = true;

    for (int i = 1; i < n; ++i) {
        for(int j = w + 10 - items[i]; j >= 0; j--)
            if(states[j])
                states[j + items[i]] = true;
    }
    for(int i = 0; i <= w; i++)
        cout<<states[i]<<" ";
    cout<<endl;
    int j;
    for (j = w; j < w + 10; ++j) {
        // 输出结果大于等于w的最小值
        if (states[j] == true)
            return j;
    }
    // 没有可行解
    if (j == w + 10 + 1)
        return -1;
}

int main()
{
    int value[6] = {34, 23, 81, 74, 57, 65};
    cout<<girlLove(value, 6, 200);
    cout<<endl;
    cout<<girlLovePlus(value, 6, 200);
    return 0;
}
原文地址:https://www.cnblogs.com/flyingrun/p/13505584.html