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;
}