問題描述:
由於《哈利波特》系列相當暢銷,店長決定通過**活動來回饋讀者。上櫃的《哈利波特》瓶裝本系列中,一共有5卷。假設每一捲單獨銷售均需8歐元。如果讀者一次購買不同的兩卷,就可以扣除5%的費用,三卷更多。具體的折扣如下:
本數 折扣
2 5%
3 10%
4 20%
5 25%
每本書只能享受一種折扣。例如買5本不同的書,可以享受折扣25%,也可以享受(5%+10%),或者(0+20%),甚至只要你願意,可以享受0折扣(分開買....);有如買了3本1卷,2本2卷,不能享受25%的折扣,因為上述的折扣只是針對不同的卷冊時才行。
問題是,如何進行買書花費最少(根據要買的書,進行折扣的組合,時折扣最大)?
書上列出了乙個10本書以內的表。發現5+3組合小於4+4的組合,由於這樣的情況出現,使得我們不能用傳統的貪心演算法解決該問題(每次貪心25%折扣)。
結合書的講解,以及自己的思考,提出下面的演算法:
傳統的貪心演算法:首先每本書的**一樣,那麼意思就是買(1,1,3,2,1)與買(1,1,1,2,3)或者(3,2,1,1,1)沒有區別。因此我們按降序排序(為了方便我們的貪心,因為5本不同的情況必然小於4本不同,4本不同又小於3本不同……)。
按降序排序後,我們要買書的情況為:
(y1,y2,y3,y4,y5) (y1≥y2≥y3≥y4≥y5)
傳統貪心,貪心區域性最優——總是選擇25%的折扣,如果沒有5本不一樣,則選擇次大的20%,如果沒有4本不一樣的,選擇10%,……
因此:剩餘
第一次選擇:y5次5本不同: y5*(1,1,1,1,1) (y1-y5,y2-y5,y3-y5,y4-y5,0)
第二次選擇:y4-y5次4本不同:y4-y5*(1,1,1,1,0) (y1-y4,y2-y4,y3-y4,0,0)
第三次選擇:y3-y4次3本不同:y3-y4*(1,1,1,0,0) (y1-y3,y2-y3,0,0,0)
第三次選擇:y2-y3次3本不同:y2-y3*(1,1,0,0,0) (y1-y2,0,0,0,0)
但是4+4的組合大於5+3,因此我們不能這麼簡單的貪心。應該是把k次5本不同與k次3本不同轉化為2k次4本不同,由此,結合上面的傳統貪心,就應該以y5與y3-y4的最小者為k,那麼本來y4-y5次4本不同就變成了y4-y5+2k次,其他不變。我也根據這樣的貪心策略寫出了程式,核心**如下:
double minprice(int *book,int n)
shellsort(book,n); // 採用希爾排序使輸入排序
intmin = (book[5] < book[3] - book[4])?book[5]:(book[3] - book[4]); // 找到k
doubleminprice;
minprice= 8*5*0.25*(book[5]-min) +
8*4*0.2*(book[4]-book[5]+2*min)+
8*3*0.1*(book[3]-book[4]-min)+
8*2*0.05*(book[2]-book[3]);
intsum = 0;
for(inti = 1; i < 6; i++)
sum+= book[i];
returnsum*8 - minprice;
輸出結果:
答案和書上的結果一樣,起組合形式4+4+4+4+4+2+1,這種組合的結果就是151.2。但是書上沒有給出證明,即5+3轉換為4+4總是最優的。
我嘗試證明一下:
假設我們一共買x本書,暫且不考慮x本的分布。如果這x本書中存在(2,2,2,1,1)的組合,那麼,一定存在5+3的組合,即(1,1,1,1,1)與(1,1,1,0,0)。
但是對於(1,1,1,1,1)與(1,1,1,0,0),一定可以分為(1,1,1,1,0)與(1,1,1,1,0),即4+4組合。因此不管是採用5+3還是4+4,總是剩下x-8本書,而且,這x-8本書的分布一定是一樣的,因為不管是(1,1,1,1,1)與(1,1,1,0,0)還是(1,1,1,1,0)與(1,1,1,1,0),都是減少了(2,2,2,1,1)。
因此0.35+f(x-8)max
< 0.4+f(x-8)max
問題的關鍵在於不管是5+3與4+4之間的轉換,始終不會破壞剩餘書本的分布。
結論:5+3轉換為4+4總是最優!
動態規劃
要用動態規劃解答首先要找到,動態規劃的遞迴公式,因為動態規劃是自頂向下層層遞迴,然後自底向下層層解答!最後根據底層結論求解最後結果。
五卷書的**相同都是8歐元,所以購買(1,0,0,0,0)跟(0,1,0,0,0)效果一樣。這裡就可以簡化為,讓所購買書按照本書遞增(遞減),從而方便討論。
要處理的引數為購買每種卷的個數,所以遞迴一定跟這五個引數相關。可以把引數按照從小到大順序排列。討論不為0的引數的個數,從而求出所有可能的折扣種類。然後從當前折扣種類中取**最小值。
(x1,x2,x3,x4,x5)代表購買每卷的個數,f(x1,x2,x3,x4,x5)代表最低**。x1< x2 < x3 < x4 < x5
f(x1,x2,x3,x4,x5)=0 ;當所有引數都為0的情況(這也是退出遞迴的出口)
f(x1,x2,x3,x4,x5)= min{
5*8*(1-25%) +f(x1-1,x2-1,x3-1,x4-1,x5-1) //引數全部 > 0
4*8*(1-20%) +f(x1,x2-1,x3-1,x4-1,x5-1) //x2 > 0
3*8*(1-10%) +f(x1,x2,x3-1,x4-1,x5-1) //x3> 0
2*8*(1-5%) +f(x1,x2,x3,x4-1,x5-1) //x4> 0
8 +f(x1,x2,x3,x4,x5-1) //x5> 0
重新開始戰鬥05 程式設計之美 光影切割
問題描述 假設有乙個矩形區域,有若干條直線切割該區域,並且沒有一條直線與y軸平行,且不存在三條 以及3條以上 切割線相交於一點的情況。請問該矩形平面被分割成多少塊。分析 假設一塊矩形區域已經被切割成很多塊,那麼此時再增加一條切割線,新的切割線與其他切割線相交,且有m個交點。那麼新的切割線被分割成m ...
重新開始戰鬥04 程式設計之美 尋找ID問題
問題描述 乙個很大的列表 有10億多個數 這個列表中全是都是id號,正常狀態下每個id都會再列表中出現兩次 都是亂序 1.當有乙個id號丟失時,如何找到這個id號 2.當有兩個id號丟失時,如何找到這兩個id號。解法一 最直觀 申請乙個陣列,這個陣列和id列表一樣大,然後遍歷id列表,每遍歷乙個id...
重新開始戰鬥06 程式設計之美 電梯排程問題
問題描述 一座不高的樓裡有一部電梯,為了解決電梯擁擠的問題,電梯的設計者規定每次電梯從一層往上走時,只允許電梯停在其中的某一層。所有的乘客都從一樓上電梯,到達某層樓後,電梯停下來,所有乘客再從這裡爬樓梯道自己的目的層。在一樓的時候,每個乘客選擇自己的目的層,電梯則自動計算出應停的樓層。學模型 設fl...