廢話:
最近剛買了本書《編程之美》,首先看了下時間:2008.3。剛好是大二的時候,真希望回到那時,買一本《編程之美》,坐在宿舍,吃着熱乾麪,編着代碼。剎那間,有種相見恨晚的感覺,前一陣,也感覺自己浮誇的很,什麼流行就看什麼。是時候按下心來,好好享受下beauty of programming
本文來自:http://blog.csdn.net/lengzijian/article/details/7789222
問題:
某書店對《哈利波特》做促銷活動,一共有5卷。假設每一卷的單獨銷售價格爲8元,如果讀者一次購買不同的兩卷,就可以扣除5%的費用,三卷則更多,優惠如下:
本數
折扣
2
5%
3
10%
4
20%
5
25%
求出購買一批書的最低價格?
剛拿過題目第一感覺就是用貪心方法,不出所料書中最開始提到的就是貪心規則。首先看下10本數以內的折扣表:
本數
所有組合
折扣
節省的錢
2-5
2
3
4
5
0.1
0.3
0.8
1.25
0.8
2.4
6.4
10
6
5+1
4+2
3+3
2+2+2
1.25
0.9
0.6
0.3
10
7.2
4.8
2.4
7
5+2
4+3
3+2+2
1.35
1.1
0.5
10.8
8.8
4
8
5+3
4+4
3+3+2
2+2+2+2
1.55
1.6
0.7
0.4
12.4
12.8
5.6
3.2
9
5+4
5+2+2
4+3+2
3+3+3
2.05
1.45
1.2
0.9
16.4
11.6
9.6
7.2
10
5+5
4+4+2
4+3+3
2+2+2+2+2
2.5
1.7
1.4
0.5
20
13.5
11.2
4
這裏不再講解貪心算法,我想記錄下動態規劃法幾個比較難理解的地方。
注:書中用的是最少花費,而我用的是最多節省錢數。之後可以通過表達式看出區別。
1. 在書中,假設用Xn表示購買第n卷數的數量,那麼購買所有書數量可以用(X1,X2,X3,X4,X5)表示,而F(X1,X2,X3,X4,X5)表示最多節省錢數。爲了使書籍沒有編號順序,即(X1,X2,X3,X4,X5)和(X3,X2,X1,X4,X5)爲一樣的變量,書中使用的“最小表示”。Y1,Y2,Y3,Y4,Y5是X1,X2,X3,X4,X5的重新排列,滿足Y1>=Y2>=Y3>=Y4>=Y5。在之後的所有闡述中,購買數的數量均以“最大滿足”方法,放入變量。例如:
我要買8本書,那麼變量的存儲值爲(2,2,2,1,1);
如果我要買10本書,那麼變量存儲值爲(2,2,2,2,2),爲何不是(5,5,0,0,0)
以一種循環+1,最大滿足所有變量的方式賦值,爲什麼如此?
個人拙見:
以10本書爲例,當用(5,5,0,0,0)爲變量時,會有5種(1+2)的方法(1+2:表示第一卷加上第二卷的組合方式),而同樣,當用(2,2,2,2,2)爲變量時,可以有([1+2] ,[2+3] ,[3+4] ,[4+5] ,[5+1])共5種兩兩組合,即所有的組合方式都是“做大滿足”方法的子集,都可以用“做大滿足”方法模擬其他變量存儲方式。
2. 假定要買的書爲(Y1,Y2,Y3,Y4,Y5),如果第一次考慮爲5本不同卷付錢(Y5>=1),那麼剩下數的集合爲(Y1-1,Y2-1,Y3-1,Y4-1,Y5-1);但是如果要爲4本不同卷付錢,剩下幾何應該有如下種:
(Y1-1,Y2-1,Y3-1,Y4-1,Y5);
(Y1-1,Y2-1,Y3-1,Y4,Y5-1);
(Y1-1,Y2-1,Y3,Y4-1,Y5-1);
(Y1-1,Y2,Y3-1,Y4-1,Y5-1);
(Y1,Y2-1,Y3-1,Y4-1,Y5-1);
我們該如何選擇然後繼續分析下去呢?
舉例說明:
假設Y1=Y2=Y3=Y4=Y5=2.
選擇(1,1,1,1,0),剩下(Y1-1,Y2-1,Y3-1,Y4-1,Y5)的組合爲(1,1,1,1,2),剩下的書爲{1,2,3,4,5,5};
選擇(1,1,1,0,1),剩下(Y1-1,Y2-1,Y3-1,Y4,Y5-1)的組合爲(1,1,1,2,1),剩下的書爲{1,2,3,4,4,5};
由於Y4>=Y5>Y5-1,後者方案中,總會有一種方案裏只有4卷而沒有5卷,完全可以把只有4沒有5的組合,換成5卷。例如:
{1,2,3,4}{4,5}->{1,2,3,5}{4,5}
對於任何(Y1-1,Y2-1,Y3-1,Y4,Y5-1)的最優解,都能轉化爲(Y1-1,Y2-1,Y3-1,Y4-1,Y5)的解。
有了上面兩個依據,就可以寫出推導公式:
因爲我求的是最大節約錢數,所以跟書上有一點不同
F(Y1,Y2,Y3,Y4,Y5)
if(Y1=Y2=Y3=Y4=Y5=0)
return 0;
return max(
5*8*25% + F(Y1-1,Y2-1,Y3-1,Y4-1,Y5-1) (Y5 >=1),
4*8*20% + F(Y1-1,Y2-1,Y3-1,Y4-1,Y5) (Y4>=1),
3*8*10% + F(Y1-1,Y2-1,Y3-1,Y4,Y5) (Y3 >=1),
2*8*5% + F(Y1-1,Y2-1,Y3,Y4,Y5) (Y2 >=1),
1*8*0% + F(Y1-1,Y2,Y3,Y4,Y5) (Y1 >=1)
)
記得要轉化成對應的最小表示(書上很清楚,這裏就不贅述了):
F(1,2,2,2,2)----------轉化-----------> F(2,2,2,2,1)
下面是我實現的c語言版本:
|
查看運行結果:
當爲10本書時:int a[5] = {2,2,2,2,2};
result 20.00
當爲8本書時:int a[5] = {2,2,2,1,1};
result 12.80
與之前表裏裏面的數據一模一樣,安心了(話說,調試遞歸什麼的最討厭了)。