最近刚买了本书《编程之美》,首先看了下时间: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语言版本:
[cpp] view plain copy print ?
|
查看运行结果:
当为10本书时:int a[5] = {2,2,2,2,2};
result 20.00
当为8本书时:int a[5] = {2,2,2,1,1};
result 12.80
与之前表里里面的数据一模一样,安心了(话说,调试递归什么的最讨厌了)。