動態規劃(三)——如何巧妙解決“雙十一”購物時的湊單問題?
阿新 • • 發佈:2020-08-15
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 迴圈重複計算的問題,而且當揹包限制過大的時候,結果會出錯。
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; }