日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

c++如何将int数组中的值取出*号运算符_如何用动态规划巧妙解决 “双十一” 购物时的凑单问题?羊毛薅起来!!!...

發布時間:2023/12/2 c/c++ 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++如何将int数组中的值取出*号运算符_如何用动态规划巧妙解决 “双十一” 购物时的凑单问题?羊毛薅起来!!!... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方“程序員大白”,選擇“星標”公眾號

重磅干貨,第一時間送達

今年過去的 “雙十一” ,你有薅到羊毛嗎?

每年的雙十一,會有各種促銷活動,比如 “滿 300元減 80 元”。假如你女朋友的購物車中有 n 個(n > 100)想買的商品,她希望從里面選幾個,在湊夠滿減條件的前提下,讓選出來的商品價格總和最大程度地接近滿減條件(300 <= price <= 380),這樣就可以極大限度地“薅羊毛”。作為一名 ”聰明“ 的程序員,你有想過編程幫她搞定嗎?

要想高效地解決這個問題,就要用到我們今天講的 01 背包問題(0-1 Knapsack Problem)。首先記住一點,01 背包問題 不是一個問題,而是一類動態規劃問題,很多動態規劃問題都可以抽象成 01 背包問題。

問題描述

01 背包問題:給定 件不可分割的物品和一個背包。物品 的重量是 w[i] ,其價值為 v[i] ,背包的容量為 。問應如何選擇裝入背包中的物品,使得裝入背包中的物品在不超過背包容量的情況下總價值最大?

在選擇裝入背包的物品時,對每種物品 只有兩種選擇,即裝入背包(1)和不裝入背包(0)。不能將物品裝入背包多次,也不能只裝入商品的一部分(商品不可分割)。這就是經典的 0-1 背包問題 。

問題的 形式化描述 是,給定 ,要求找出一個 n 元 0-1 向量 ?,使得 ,而且 達到最大。因此,0-1背包問題是一個特殊的整數規劃問題:

0-1 背包問題(簡化版)

為了理解的方便,我們可以將原 01 背包問題簡化一下:

給定 件不可分割的物品和一個背包。物品 的重量是 w[i] ,背包的容量為 。問應如何選擇裝入背包中的物品,請問裝入背包的所有物品的最大重量是多少?

問題的形式化描述是,給定 ,要求找出一個 n 元 0-1 向量 ?,求 ?的最大值。因此,0-1背包問題是一個特殊的整數規劃問題:

考慮一個簡單輸入示例:

背包容量 c = 10

物品個數 n = 5

物品重量為 wt[] = {2,2,4,6,5}

我們將題目中的物品價值暫時去掉了,這樣更方便我們掌握動態規劃和 01 背包問題,我們先考慮用遞歸對問題進行解決。

暴力遞歸就是枚舉物品集合的所有子集,并計算每一個子集的總重量,最后選擇與背包的總容量 最接近的子集即為最優解。

考慮物品的最優子集,對于每一個物品 均有下面兩種情況。

  • 物品 包含在最優子集中
  • 物品 不包含在最優子集中。
  • 如果添加第 n 個物品后,背包的重量超過了總容量 ,則第 n 個物品就不能裝入背包;否則,則可以將第 n 個物品裝入背包。

    回顧一下遞歸的三要素(詳細內容可參考 數據結構與算法之遞歸 + 分治 ,本文的重點是如何雙十一薅羊毛!):

    第一:明確你寫的遞歸函數想要干什么,即函數功能;

    第二:找到遞歸的結束條件;

    第三:找出函數的等價關系式。

    class?Knapsack{
    ????private?int?maxW?=?Interger.MIN_VALUE;?//?保存背包中可容納的最大重量
    ????//?w?表示當前已經裝進背包的物品的總重量;?i表示考察到了哪個物品?i
    ????private?int[]?wt?=?{2,2,4,6,5};?//表示每一個物品的重量
    ????private?n?=?5;?//?n?表示物品總數
    ????private?c?=?9;?????//?c?背包容量?
    ????//第一要素:函數功能,決定是否將第 i 個物品裝入背包,從而獲得最大重量
    ????public?void?Knapsack(int?i,?int?w){
    ????????//遞歸結束條件
    ????????if(w?==?c?||?i?==?n){?//?w?==?c?表示裝滿了,i?==?n?物品考察完了
    ????????????if(w?>?maxW){
    ????????????????maxW?=?w;
    ????????????}
    ????????????return;
    ????????}
    ????????//?等價關系式,裝?or?不裝
    ????????Knapsack(i+1,?w);?//?選擇不裝第?i?個物品
    ????????if(w?+?wt[i]?<=?c){
    ????????????Knapsack(i+1,?w?+?wt[i]);?//?選擇裝第 i 個物品。
    ????????}
    ????}}

    遞歸回溯算法的代碼雖然看著簡潔明了,但是其時間復雜度比較高,是指數級別的。為了更清晰地看到其低效的原因,老規矩,畫出遞歸樹。我們依舊使用輸入示例,畫出遞歸樹如下:

    遞歸樹中的每一個結點表示一種狀態,我們用 (i, w) 來表示,其中,i 表示將要決策的第 i 個物品是否裝入背包,w 表示當前背包中物品的總重量。比如,(3,8) 表示我們要決策的物品第 3 個物品(重量為 4)是否裝入背包,決策后,將其裝入背包,當前背包的重量為 8;(3,4) 則表示我們當前要決策的物品是第 3 個物品,在決策后,不將其裝入背包,當前背包的重量為 4.

    顯而易見,遞歸樹中有很多子問題被重復計算,比如圖中的 f(2,2) 和 f(3,4) 均被重復計算了兩次。

    要對這些重復計算的結點進行剪枝,我們就可以使用 DP Table 和備忘錄方法。

    “備忘錄” 方法,就是將已經計算好的子問題的解 f(i, w) 保存起來,當再次計算到重復的 f(i, w) 時,直接從備忘錄中取出來用就行了,不用再遞歸計算,這樣就有效地避免重復計算,達到剪枝效果。

    class?Knapsack{
    ????private?int?maxW?=?Interger.MIN_VALUE;?
    ????
    ????private?int[]?wt?=?{2,2,4,6,5};?
    ????private?n?=?5;?
    ????private?c?=?9;
    ?private?boolean[][]?mem?=?new?boolean[5][10];?//備忘錄,默認均為?false
    ????public?void?Knapsack(int?i,?int?w){
    ????????//遞歸結束條件
    ????????if(w?==?c?||?i?==?n){?//?w?==?c?表示裝滿了,i?==?n?物品考察完了
    ????????????if(w?>?maxW){
    ????????????????maxW?=?w;
    ????????????}
    ????????????return;
    ????????}
    ?? if(mem[i][w])?return;?//?重復狀態
    ????????
    ????????mem[i][w]?=?true;?//?記錄狀態
    ????????
    ????????Knapsack(i+1,?w);?//?選擇不裝第?i?個物品
    ????????if(w?+?wt[i]?<=?c){
    ????????????Knapsack(i+1,?w?+?wt[i]);?//?選擇裝第 i 個物品。
    ????????}
    ????}
    }

    備忘錄方法是自頂向下的方法,與遞歸的結構一致,且其在性能方面和動態規劃的基本一致。但我們主要學習的是動態規劃,所以進入今日的主角。

    我們把原問題的整個求解過程分為 n 個階段,每個階段會決策一個物品是否放到背包中。每個物品決策(放入或者不放入背包)完之后,背包中的物品的重量會有多種情況,也就是說,會達 到多種不同的狀態,對應到遞歸樹中,就是有很多不同的節點。

    我們把每一層重復的狀態(節點)合并,只記錄不同的狀態,然后基于上一層的狀態集合, 來推導下一層的狀態集合。我們可以通過合并每一層重復的狀態,這樣就保證每一層不同狀 態的個數都不會超過 c 個(c 表示背包的承載重量),也就是例子中的 9。于是,我們就 成功避免了每層狀態個數的指數級增長。

    我們用一個二維數組 dp[n][w+1],來記錄每層可以達到的不同狀態。

    第 0 個(下標從 0 開始編號)物品的重量是 2,要么裝入背包,要么不裝入背包,決策完 之后,會對應背包的兩種狀態,背包中物品的總重量是 0 或者 2。我們用 dp[0] [0] = 1 和 dp[0][2] = 1 ?來表示這兩種狀態。這實際上就是我們原問題里面的 n 元 0-1 向量 。即 (1,0,1,0,0,0,0,0,0) ?。

    對于第 1 個物品的重量也是 2, 基于之前的背包狀態,在這個物品決策完之后,不同的狀態有 3 個,背包中物品總重量分別是 0(0+0),2(0+2 or 2+0),4(2+2)。我們用 dp[1][0] = 1,dp[1][2] = 1,dp[1][4] = 1 來表示這三種狀態。即 (1,0,1,0,1,0,0,0,0) ?。

    以此類推,直到決策完所有的物品后,整個 DP Table 就算都計算好了。我們可以自己計算一遍,就可以得到下面的 DP Table 了,我們只需要在決策完最后一件物品的最后一行,找出值為 1 的最接近 c (這里是 9)的值,就是可以裝入背包中物品的總重量的最大值。

    實現代碼其實就是填表的整個過程,你能自己手填出此表,看代碼簡直輕而易舉。

    class?Knapsack{
    ????private?int?maxW?=?Interger.MIN_VALUE;?
    ????
    ????private?int[]?wt?=?{2,2,4,6,5};?
    ????private?n?=?5;?
    ????private?c?=?9;
    ????
    ????public?int?Knapsack(int[]?wt,?int?n,?int?c){
    ??boolean[][]?dp?=?new?boolean[n][c+1];?//?默認為false,即為0
    ????????
    ????????dp[0][0]?=?true;?//?初始狀態
    ????????dp[0][wt[0]]?=?true;?//?第一行數據,也就是起始狀態,特殊處理。
    ????????
    ????????//從決策第二個物品開始,自底向上?DP?Table
    ????????for(int?i?=?1;?i?????????????for(int?j?=?0;?j?<=?c;?j++){
    ?????????????if(dp[i-1][j]){?//?不裝入第?i?個物品
    ????????????????????dp[i][j]?=?dp[i-1][j];
    ????????????????}???????????
    ????????????}
    ????????????//裝入第?i?個物品
    ????????????for(int?k?=?0;?k?<=?c?-?wt[i];?k++){
    ????????????????if(dp[i-1][k]){
    ????????????????????dp[i][k?+?wt[i]]?=?true;
    ????????????????}
    ????????????}
    ????????}
    ????????
    ????????for(int?i?=?c;?i?>=?0;?i--){
    ????????????if(dp[n-1][i])?return?i;
    ????????}
    ????????return?0;
    ????}
    }

    這就是基于 DP Table 的動態規劃的自底向上的填表過程,把問題分解成多個階段,每個階段對應一個決策,我們記錄每一個階段所有可達的狀態集合,然后用前面階段已經得到的狀態來推導當前狀態集合,動態地向前推進,這就是動態規劃的由來,雖然還挺貼切,但是還是 DP Table 來的舒服!

    已知暴力遞歸,枚舉所有可能的組合的時間復雜度為指數級別的 ,基于 DP Table 的動態規劃的時間復雜度為 ,其中 n 表示物品的個數,而 c 表示可以背包的總容量(Capacity)。

    但是聰明的你也一定發現了一個問題,剛才的代碼中的 DP Table 是一個二維數組,而且我們事實上,當我們決策第 i 個物品是否裝入背包的狀態時,僅使用到了其前一個狀態 i - 1 的狀態值,所以我們只需要一個大小為 c+1 的一維 DP Table 就可以解決這個問題。

    我們可以仔細觀察一下上面代碼中注釋 不裝入第 i 個物品 的情況,拿到不就是將第 i - 1 個物品的狀態直接拷貝到第 i 個物品對應的狀態數組中嗎?比如,初始狀態(即決策了第 0 個物品之后的狀態集合)為:

    現在要決策是否將第 1 個物品(重量也為 2)是否裝入背包,我們會考慮兩種情況:

  • 將物品不裝入背包,則此時第 1 個物品的狀態集合與第 0 個物品的狀態集合一樣,按照之前的二維 dp 數組,會將其拷貝一次,但是現在 dp 數組變成了一維,我們有必要再拷貝一次嗎?
  • 將物品裝入背包,此時我們直接對一維的 dp[] 數組進行修改不就可以了嗎?
  • 所以要將原來使用的二維 DP Table 變成 一維的,只需要考慮裝入的情況,然后直接對一維的 DP Table 進行修改即可。

    簡單來說,你可以像下圖這樣理解,原來一個二維的 dp[][] 會記錄所有階段的狀態值,而現在一維的 dp[] 只記錄當前決策的物品的所有狀態值:

    代碼如下:

    class?Knapsack{
    ????private?int?maxW?=?Interger.MIN_VALUE;?
    ????
    ????private?int[]?weight?=?{2,2,4,6,5};?
    ????private?n?=?5;?
    ????private?c?=?9;
    ????
    ????public?int?Knapsack(int[]?weight,?int?n,?int?c){
    ????????boolean[]?dp?=?new?boolean[c+1];?//?默認為false,即為0
    ????????dp[0]?=?true;?//?初始狀態
    ????????dp[weight[0]]?=?true;?//?第一行數據,也就是起始狀態,特殊處理。
    ????????//從決策第二個物品開始,自底向上?DP?Table
    ????????for(int?i?=?1;?i?????????????//裝入第?i?個物品
    ????????????for(int?j?=?c?-?weight[i];?j?>=?0?;?--j){
    ????????????????if(dp[j]){
    ????????????????????dp[j+weight[i]]?=?true;
    ????????????????}
    ????????????}
    ????????}
    ????????
    ????????for(int?i?=?c;?i?>=?0;?i--){
    ????????????if(dp[i])?return?i;
    ????????}
    ????????return?0;
    ????}
    }

    這里一定要注意內層循環控制變量 j 是從 c - weight[i] 開始由大到小進行逆序遍歷,因為 j 從小到大順序遍歷的話,會出現 for 循環重復計算和值被覆蓋的情況。

    我們以最后一次更新(即決策第 4 個物品是否裝入背包)為例進行說明,更新前(第 3 個物品的狀態集合) dp[] 數組的狀態為:

    現在更新第 4 個物品(重量為 5)是裝入背包后的狀態集合,我們先看看從小到大進行處理的會發生什么?

    j = 0 時,dp[0] = 1 ,則將 dp[j + weight[i]] = dp[0+5] = true

    j = 1 時,dp[1] = 0 ,跳過;

    j = 2 時,dp[2] = 1 ,則將 dp[2 + 5] = dp[7] = 1

    j = 3 時,dp[3] = 0 ,跳過;

    j = c - weight[i] = 9 - 5 = 4 時,dp[4] = 1 ,則將 dp[4 + 5] = dp[9] = 1

    這里似乎看不出來什么,但是當物品的數目比較多,背包的容量比較大的時候,就會出現某一個值 dp[k] 依賴與它前面的某一個狀態 dp[x] 的情況,我們為了避免使用更新后的狀態 dp[x] 去更新 ?dp[k] 的情況才從大到小進行計算的。

    雖然對于這個例子,你看到正序和逆序都不會影響最終的結果,但我希望你銘記:采用一維 DP Table 對狀態進行保存并更新時,內層循環一定要從大到小進行更新!

    現在讓我們回到原始的 0-1 背包問題。

    01 背包問題

    問題的形式化描述是,給定 ,要求找出一個 n 元 0-1 向量 ?,使得 ,而且 達到最大。因此,0-1背包問題是一個特殊的整數規劃問題:

    最優子結構

    設 是所給 0-1 背包問題的一個最優解,則 是下面相應子問題的一個最優解:

    (反證法)如若不然,設 ? 是上述子問題的一個最優解,而 ? 不是它的最優解,由此可知, ,且 。因此, ,這說明 ? 是所給 0-1 背包問題的一個更優解,從而 ? 不是所給 0-1 背包問題的最優解。此為矛盾。

    所以 0-1 背包問題具有最優子結構性質。

    遞歸關系

    設所給 0-1 背包問題的子問題為:

    子問題的最優值為 ,即 ? ?是背包容量為 j,可選物品為 i,i+1,...,n 時 0-1 背包問題的最優值。由 0-1 背包問題的最優子結構性質,可以建立計算 ? 的遞歸式如下:

    請不要拒絕數學推導和動態規劃轉移方程的數學表達,這才是真正的動態規劃,只是我們為了應付面試、筆試才在網上看到各種模板解題套路,真正的套路是自己內化而成的!

    遞歸法

    有了上面的狀態轉移方程,不難寫出下面的遞歸代碼:

    class?Knapsack?{?
    ??
    ????//?返回兩個整數的較大值
    ????static?int?max(int?a,?int?b)??{??
    ??????return?(a?>?b)???a?:?b;??
    ????}?
    ??
    ????//?第一要素:明確你寫的遞歸函數想要干什么
    ????//?函數功能:計算可以放入容量為 W 的背包的最大價值
    ????//?W:背包容量,wt[]:每一個物品的重量,val[]:每一個物品所對應的價值
    ????static?int?knapSack(int?W,?int?wt[],?int?val[],?int?n)?{?
    ????????//?第二要素:找到遞歸的結束條件(一定要考慮全面)
    ????????//?背包容量?W?為?0?或者物品個數為?0?,可獲得的價值必然為0
    ????????if?(n?==?0?||?W?==?0)?
    ????????????return?0;?
    ??
    ????????//?如果第?n?個商品的重量大于背包容量?W,則該商品不能包含在最優解中
    ????????if?(wt[n?-?1]?>?W){
    ????????????return?knapSack(W,?wt,?val,?n?-?1);?
    ????????}
    ????????//?第三要素:找出函數的等價關系式:取?(1)?和?(2)?的較大值
    ????????//?(1)?val[n?-?1]??+?knapSack(W?-?wt[n?-?1],?wt,?val,?n?-?1)?包含第?n?個物品
    ????????//?(2)?knapSack(W,?wt,?val,?n?-?1)?不包含第?n?個物品
    ????????else{
    ????????????return?max(val[n?-?1]??+?knapSack(W?-?wt[n?-?1],?wt,?val,?n?-?1),?knapSack(W,?wt,?val,?n?-?1));?
    ????????}
    ????}?
    }?

    可以看到遞歸函數重復計算了子問題的解。如下所示的遞歸樹,K(1,1) 被計算了兩次。

    • 時間復雜度為 。
    • 空間復雜度為

    注意:0-1背包問題如果用我們前面提到的示例輸入解釋比較復雜,我們選擇了一個更簡單的輸入,讓大家理解 0-1 背包問題!

    0-1 背包問題輸入示例:

    wt[ ] = {1, 1, 1} // 物品的重量

    W = 2 // 背包的容量

    val[ ] ?= {10, 20, 30} // 物品對應的價值

    下圖中的遞歸樹表示的是 K(i, W) ?表示背包重量為 W,可選擇物品為 ?i,i+1,...,n 時 0-1 背包問題的最優值。

    其實,拋開了物品的價值,這個遞歸樹就和前面講的簡化版本一個樣,沒啥稀奇的。

    動態規劃方法

    緊接著模仿簡化的 0-1 背包問題,創建一個二維的 DP[][] 數組,用來記錄每層可以達到的不同狀態(決策第 i 個物品,背包從容量 1 到 ?W 的所有狀態值)。不過這里數組存儲的值不再是布爾類型了,而是當前狀態對應的最大總價值。

    class?Knapsack?{?
    ????
    ????static?int?max(int?a,?int?b)??{??
    ??????????return?(a?>?b)???a?:?b;??
    ????}?
    ??
    ????//?返回容量為?W?的背包可容納的最大價值?
    ????static?int?knapSack(int?W,?int?wt[],?int?val[],?int?n)?{?
    ????????int?i,?w;?
    ????????int?K[][]?=?new?int[n?+?1][W?+?1];?
    ??
    ????????//?自底向上構建?DP?Table
    ????????for?(i?=?0;?i?<=?n;?i++)??
    ????????{?
    ????????????for?(w?=?0;?w?<=?W;?w++)??
    ????????????{?
    ????????????????if?(i?==?0?||?w?==?0){
    ????????????????????K[i][w]?=?0;?????????????????????
    ????????????????}
    ????????????????else?if?(wt[i?-?1]?<=?w){?
    ????????????????????K[i][w]?=?max(val[i?-?1]?+?K[i?-?1][w?-?wt[i?-?1]],?
    ?????????????????????????K[i?-?1][w]);
    ????????????????}
    ????????????????else{
    ????????????????????K[i][w]?=?K[i?-?1][w];?????????????????????
    ????????????????}
    ????????????}?
    ????????}?
    ??
    ????????return?K[n][W];?
    ????}?
    ????
    ????public?static?void?main(String?args[])?{?
    ????????int?val[]?=?new?int[]?{?6,?10,?12?};?
    ????????int?wt[]?=?new?int[]?{?1,?2,?3?};?
    ????????int?W?=?5;?
    ????????int?n?=?val.length;?
    ????????System.out.println(knapSack(W,?wt,?val,?n));?
    ????}?
    }?
    • 時間復雜度: , 表示物品個數, 表示背包容量。
    • 空間復雜度:

    同樣的道理,我們可以將二維的 DP[][] 數組用一個一維的數組保存起來,將空間復雜度降到 。

    class?KnapSack{?
    ????
    ????static?int?KnapSack(int?val[],?int?wt[],?int?n,?int?W)?{?
    ????????//dp[i]?存儲容量為?"i"?時背包的最大價值
    ????????int[]?dp?=?new?int[W+1];?

    ????????//將?dp[]?初始化為?0
    ????????Arrays.fill(dp,?0);?

    ????????//?迭代所有物品
    ????????for(int?i?=?0;?i?????????????//從大到小遍歷dp數組并更新(和之前一樣)
    ????????????for(int?j?=?W;?j?>=?wt[i];?j--){?
    ????????????????dp[j]?=?Math.max(dp[j]?,?val[i]?+?dp[j?-?wt[i]]);
    ????????????}
    ????????}
    ????????return?dp[W];?
    ????}?
    ?
    ????public?static?void?main(String[]?args)?{?
    ????????int?val[]?=?{6,?10,?12},?wt[]?=?{1,?2,?3},?W?=?5,?n?=?3;?
    ????????System.out.println(KnapSack(val,?wt,?n,?W));?
    ????}?
    }?
    • 時間復雜度為
    • 空間復雜度為

    購物車薅羊毛

    掌握 0-1 背包問題之后,用動態規劃薅羊毛,豈不是很簡單了?

    每年的雙十一,會有各種促銷活動,比如 “滿 300元減 80 元”。假如你女朋友的購物車中有 n 個(n > 100)想買的商品,她希望從里面選幾個,在湊夠滿減條件的前提下,讓選出來的商品價格總和最大程度地接近滿減條件(300 <= price <= 380),這樣就可以極大限度地“薅羊毛”。作為一名 ”聰明“ 的程序員,你有想過編程幫她搞定嗎?

    從原問題提取出我們的輸入輸出

    輸入:

    items[ ] = {56,188,66,88,190} // n 件商品的價格 (相當于 0-1 背包問題中物品的重量)

    W = 380 // 最多可接受的湊單價格,相當于 0-1 背包問題中背包的容量。

    輸出:

    可以參與湊單的商品列表 dp ,這個過程就是向上回溯輸出的過程。

    class?Double11Collage{
    ????private?static?final?int?DISCOUNT?=?80;
    ????public?static?void?double11Collage(int[]?items,?int?n,?int?price)?{
    ????????int?W?=?price?+?DISCOUNT;
    ??????boolean[][]?dp?=?new?boolean[n][W];
    ????????
    ??????dp[0][0]?=?true;?
    ??????dp[0][items[0]]?=?true;
    ????????
    ??????for?(int?i?=?1;?i?//?動態規劃
    ?????????for?(int?j?=?0;?j?<=?W;?++j)?{//?不購買第?i?個商品
    ????????????if?(dp[i-1][j]?==?true)?dp[i][j]?=?dp[i-1][j];
    ?????????}
    ?????????for?(int?j?=?0;?j?<=?W-items[i];?++j)?{//?購買第?i?個商品
    ????????????if?(dp[i-1][j]==true)?dp[i][j+dp[i]]?=?true;
    ?????????}
    ??????}
    ??????int?j;
    ??????for?(j?=?price;?j??????????if?(dp[n-1][j]?==?true)?break;?//?輸出結果大于等于拼單價的最小值
    ??????}
    ??????if?(j?==?W)?return;?//?沒有可行解
    ??????for?(int?i?=?n-1;?i?>=?1;?--i)?{?//?i?表示二維數組中的行,j?表示列
    ?????????if(j-items[i]?>=?0?&&?dp[i-1][j-items[i]]?==?true)?{
    ????????????System.out.print(items[i]?+?"?");?//?購買這個商品
    ????????????j?=?j?-?items[i];
    ?????????}?// else 沒有購買這個商品,j 不變。
    ??????}
    ??????if?(j?!=?0){
    ????????????System.out.print(items[0]);
    ????????}?
    ????}
    }

    求可以湊成拼單價格的最低價格,不就和 0-1 背包的代碼一樣嗎?這里不能再考慮使用空間復雜度優化的動態規劃了,我們要保存決策過程中的每一個狀態,從而由決策完最后一件商品之后的最優解向上回溯,找到最優解所涉及的商品并打印出來。

    狀態 (i, j) 只有可能從 (i-1, j) 或者 (i-1, j-items[i]) 兩個狀態推導過來。所以,我們就檢查這兩個狀態是否是可達的,也就是 dp[i-1][j] 或者 dp[i-1][j-items[i]] 是否 true。

    如果dp[i-1][j] 可達,就說明我們沒有選擇購買第 i 個商品,如果 ?dp[i-1][j-items[i]] 可達,那就說明我們選擇了購買第 i 個商品。我們從中選擇一個可達的狀態(如果兩個都可 達,就隨意選擇一個),然后,繼續向上回溯,看其他商品是否有選擇購買并打印輸出。

    PS:其實我發現,女朋友比動態規劃更牛掰,原價129 在李佳琦直播間 89元,結果女朋友又買了另外一件商品湊了個單,愣是將 129 減到了 69 元。最后悄悄退個單就 Okay 了,還是我女朋友厲害(感慨)。

    總結

    就買東西而言,動態規劃在女朋友前不堪一擊!期待你將其優化,證明一波學習比戀愛更有趣!

    正題:0-1背包問題是一類問題,可以說,讓你在理解的基礎之上將上面解決0-1背包的三種代碼(遞歸、動態規劃、空間優化的 DP)背下來也不為過,所以多看幾遍,在理解的基礎上把 0-1 背包的代碼多默寫幾遍!

    see you next time!

    推薦閱讀

    張一鳴:每個逆襲的年輕人,都具備的底層能力

    色情版“微信”背后的秘密

    200元人民幣面世!

    “打工人”梗刷爆網絡,今天你打工了嗎?

    關于程序員大白

    程序員大白是一群哈工大,東北大學,西湖大學和上海交通大學的碩士博士運營維護的號,大家樂于分享高質量文章,喜歡總結知識,歡迎關注[程序員大白],大家一起學習進步!

    總結

    以上是生活随笔為你收集整理的c++如何将int数组中的值取出*号运算符_如何用动态规划巧妙解决 “双十一” 购物时的凑单问题?羊毛薅起来!!!...的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。