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)是否裝入背包,我們會考慮兩種情況:
所以要將原來使用的二維 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数组中的值取出*号运算符_如何用动态规划巧妙解决 “双十一” 购物时的凑单问题?羊毛薅起来!!!...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网易宣布成立 T-MINUS ZERO
- 下一篇: 分红和派息的区别?