《編程之美》買書問題及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語言版本:

#include<stdio.h>
//折扣信息(逆序)
//個人不喜歡float,所以在結果後除以100就可以了                                                                                               
int change[5] = {25,20,10,5,0};
 
//求數組中最大的值
//用於取出做大節約錢數
int max_index(int a[5]){
    int max=0;
    int i;
    for(i=0;i<5;i++){
        if(max < a[i])
            max = a[i];
    }
    return max;
}
//冒泡排序
//用於轉化最小表示(1,2,2,2,2)->(2,2,2,2,1)
void bubble(int *a,int n)
{
    int i,j,temp;
    for(i=0;i<n-1;i++)
        for(j=i+1;j<n;j++)
            if(a[i]<a[j]) {
                temp=a[i];
                a[i]=a[j];
                a[j]=temp;
            }
}
 
//遞歸函數
int func(int a[5]){
    int i,j,k;
    int buf[5];
    int cash[5];
    for(i=0;i<5;i++){
        cash[i] = 0;
        //buf[i] = a[i];
    }
    //退出條件
    if(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4]&&a[4] == 0){
        return 0;
    }
    //排序 轉化最小表示
    bubble(a,5);
    //5次循環
    for(i=1;i<=5;i++){
        if(a[5-i] >= 1){
            for(k=0;k<5;k++){
                buf[k] = a[k];
            }
            //減一
            for(j=0;j<=5-i;j++)
                buf[j] = buf[j]-1;
            //繼續遞歸
            cash[i-1] = (5-i+1)*8*change[i-1]+func(buf);
        }
    }
    //返回最大值
    return max_index(cash);
}
 
int main(){
    //a數組用於表示所選的方案2,2,2,2,2表示選10本書
    //這裏之前講過,爲什麼要這麼做了。。
    //讀者也可以自己寫“最大滿足”的函數
    int a[5] = {2,2,2,2,2};
    float result =0;
    result = func(a);
    printf("result %2.2f\n",result/100);
}


查看運行結果:

當爲10本書時:int a[5] = {2,2,2,2,2};

result 20.00

當爲8本書時:int a[5] = {2,2,2,1,1};

result 12.80

與之前表裏裏面的數據一模一樣,安心了(話說,調試遞歸什麼的最討厭了)。

發佈了81 篇原創文章 · 獲贊 1 · 訪問量 51萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章