《编程之美》买书问题及c语言代码实现

最近刚买了本书《编程之美》,首先看了下时间: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 ?

  1. #include<stdio.h>  
  2. //折扣信息(逆序)  
  3. //个人不喜欢float,所以在结果后除以100就可以了                                                                                                 
  4. int change[5] = {25,20,10,5,0};  
  5.    
  6. //求数组中最大的值  
  7. //用于取出做大节约钱数  
  8. int max_index(int a[5]){  
  9.     int max=0;  
  10.     int i;  
  11.     for(i=0;i<5;i++){  
  12.         if(max < a[i])  
  13.             max = a[i];  
  14.     }  
  15.     return max;  
  16. }  
  17. //冒泡排序  
  18. //用于转化最小表示(1,2,2,2,2)->(2,2,2,2,1)  
  19. void bubble(int *a,int n)  
  20. {  
  21.     int i,j,temp;  
  22.     for(i=0;i<n-1;i++)  
  23.         for(j=i+1;j<n;j++)  
  24.             if(a[i]<a[j]) {  
  25.                 temp=a[i];  
  26.                 a[i]=a[j];  
  27.                 a[j]=temp;  
  28.             }  
  29. }  
  30.    
  31. //递归函数  
  32. int func(int a[5]){  
  33.     int i,j,k;  
  34.     int buf[5];  
  35.     int cash[5];  
  36.     for(i=0;i<5;i++){  
  37.         cash[i] = 0;  
  38.         //buf[i] = a[i];  
  39.     }  
  40.     //退出条件  
  41.     if(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4]&&a[4] == 0){  
  42.         return 0;  
  43.     }  
  44.     //排序 转化最小表示  
  45.     bubble(a,5);  
  46.     //5次循环  
  47.     for(i=1;i<=5;i++){  
  48.         if(a[5-i] >= 1){  
  49.             for(k=0;k<5;k++){  
  50.                 buf[k] = a[k];  
  51.             }  
  52.             //减一  
  53.             for(j=0;j<=5-i;j++)  
  54.                 buf[j] = buf[j]-1;  
  55.             //继续递归  
  56.             cash[i-1] = (5-i+1)*8*change[i-1]+func(buf);  
  57.         }  
  58.     }  
  59.     //返回最大值  
  60.     return max_index(cash);  
  61. }  
  62.    
  63. int main(){  
  64.     //a数组用于表示所选的方案2,2,2,2,2表示选10本书  
  65.     //这里之前讲过,为什么要这么做了。。  
  66.     //读者也可以自己写“最大满足”的函数  
  67.     int a[5] = {2,2,2,2,2};  
  68.     float result =0;  
  69.     result = func(a);  
  70.     printf(“result %2.2f\n”,result/100);  
  71. }  

查看运行结果:

当为10本书时:int a[5] = {2,2,2,2,2};

result 20.00

当为8本书时:int a[5] = {2,2,2,1,1};

result 12.80

与之前表里里面的数据一模一样,安心了(话说,调试递归什么的最讨厌了)。

    原文作者:CourageK
    原文地址: https://blog.csdn.net/u011487593/article/details/47211727
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞