编程之美--买书问题(递归算法)

斐波那契数的计算公式是F(n)=F(n-1)+F(n-2),F(0)=F(1)=1

任何递推的数学公式都可以直接翻译为递归的算法,于是递归的 斐波那契数计算方法是这样的:

int fib(int n){
    if(n<=1)
        return 1;
    else
        return fib(n-1)+fib(n-2);
}

但是这样导致了大量的重复计算,造成时间和空间的极大浪费,如下图所示

把递归的算法写成非递归的算法,子问题的答案记录在一个表里,这种技巧就是动态规划。

在计算斐波那契数的时候我们只需要把F(n)和F(n+1)保留到计算出F(n+2)就可以了,不需要一直保存。改进后的算法如下:

int fibonacci(int n){
    if(n<=1)
        return 1;
    int pre=1;
    int pre_pre=1;
    int cur;
    for(int i=2;i<=n;i++){
        cur=pre+pre_pre;
        pre_pre=pre;
        pre=cur;
    }
    return cur;
}

动态规划常用于在序贯决策过程中寻求最优方案,简单地说就是依照某一个决策链如果最终状态是最优的,那么依照同样的决策链最终状态的前一个状态毕然也是最优的。比如说在下图所示的有向无环图中我们找到红色线路所示的关键路径,它是从A到G的最短路径=>它是从A到F1的最短路径=>它是从A到E2的最短路径=>......它是从A到C3的最短路径。

《编程之美》上有一道买书问题:说哈利波特这本书一共有五卷,每卷都是8欧元,如果读者一次购买不同的两卷可扣除5%的折扣,三卷10%,四卷20%,五卷25%。现在我要买很多本书,应该怎么组合才最省钱?

我们用F(Y1,Y2,Y3,Y4,Y5)表示这五卷书分别Yi本时的最少花销。由于购买2本卷一其余只购1本和购买2本卷二其余只购1本的最少花销是一样的,即F(2,1,1,1,1)=F(1,2,1,1,1)=F(1,1,2,1,1)=......。我们用F(2,1,1,1,1)来代表这一组方案的“最小表示”,即在一个最小表示F(Y1,Y2,Y3,Y4,Y5)中满足Y1>=Y2>=Y3>=Y4>=Y5。

用动态规划我们可以建立状态转移方程:

F(Y1,Y2,Y3,Y4,Y5)

=0                                                               if(Y1=Y2=Y3=Y4=Y5=0)

=min{

        40*0.75+F(Y1-1,Y2-1,Y3-1,Y4-1,Y5-1) ,                if(Y5>=1)

        32*0.8+F(Y1-1,Y2-1,Y3-1,Y4-1,Y5)  ,                    if(Y4>=1)

        24*0.9+F(Y1-1,Y2-1,Y3-1,Y4,Y5) ,                        if(Y3>=1)

        16*0.95+F(Y1-1,Y2-1,Y3,Y4,Y5) ,                         if(Y2>=1)

        8+F(Y1-1,Y2,Y3,Y4,Y5) ,                                      if(Y1>=1)

}

状态转移之后得到的F(Y1-1,Y2-1,Y3-1,Y4-1,Y5)等可能不是“最小表示”,要把它转化为对应的“最小表示”。

上代码:

001#include<stdio.h>
002#include<memory.h>
003#include<stdlib.h>
004#define INT_MAX 32767
005  
006typedef struct buy
007{
008    struct buy* next;
009    int Y1;
010    int Y2;
011    int Y3;
012    int Y4;
013    int Y5;
014}*NODE;
015  
016void BubbleSort(int *arr,int len);
017float min(float n1,float n2);
018void copy(NODE buy,int Y1,int Y2,int Y3,int Y4,int Y5);
019float foo(int Y1,int Y2,int Y3,int Y4,int Y5,NODE oldnode);
020  
021main()
022{
023    int c1=0,c2=0,c3=0,c4=0,c5=0;
024    NODE head=(NODE)malloc(sizeof(struct buy));
025    head->next=NULL;
026    copy(head,c1,c2,c3,c4,c5);
027    printf("Input copies you want buy for each book:\n");
028    scanf("%d%d%d%d%d",&c1,&c2,&c3,&c4,&c5);
029    float money=foo(c1,c2,c3,c4,c5,head);
030    printf("购买这批书的最少花销为:%.2f元\n",money);
031    printf("每一步的购书策略为:\n");
032    do
033    {
034        NODE tmp=head;
035        head=head->next;
036        free(tmp);
037        printf("%-8d%-8d%-8d%-8d%-8d\n",head->Y1,head->Y2,head->Y3,head->Y4,head->Y5);
038    }while(head->next!=NULL);
039}
040void BubbleSort(int *arr,int len)
041{
042    int i,j;
043    for(i=0;i<len-1;i++)
044    {
045        int k=0;
046        for(j=1;j<len-i;j++)
047        {
048            if(arr[j]<arr[k])
049                k=j;
050        }
051        int tmp=arr[j-1];
052        arr[j-1]=arr[k];
053        arr[k]=tmp;
054    }   
055}
056float min(float n1,float n2)
057{
058    return n1<n2?n1:n2;
059}
060void copy(NODE buy,int Y1,int Y2,int Y3,int Y4,int Y5)
061{
062    buy->Y1=Y1;
063    buy->Y2=Y2;
064    buy->Y3=Y3;
065    buy->Y4=Y4;
066    buy->Y5=Y5;
067}
068float foo(int Y1,int Y2,int Y3,int Y4,int Y5,NODE oldnode)
069{
070    if(Y1==0&&Y2==0&&Y3==0&&Y4==0&&Y5==0)
071        return 0.0f;
072    int arr[5]={0};
073    arr[0]=Y1;
074    arr[1]=Y2;
075    arr[2]=Y3;
076    arr[3]=Y4;
077    arr[4]=Y5;
078    BubbleSort(arr,5);
079    float f1=INT_MAX*1.0f,f2=INT_MAX*1.0f,f3=INT_MAX*1.0f,f4=INT_MAX*1.0f,f5=INT_MAX*1.0f;
080    NODE newnode1=(NODE)malloc(sizeof(struct buy));
081    newnode1->next=NULL;
082    copy(newnode1,1,0,0,0,0);
083    NODE newnode2,newnode3,newnode4,newnode5;
084    int t1[5]={arr[0]-1,arr[1],arr[2],arr[3],arr[4]};
085    f1=8+foo(arr[0]-1,arr[1],arr[2],arr[3],arr[4],newnode1);
086    if(arr[1]>=1)
087    {
088        newnode2=(NODE)malloc(sizeof(struct buy));
089        newnode2->next=NULL;
090        copy(newnode2,1,1,0,0,0);
091        f2=2*8*0.95+foo(arr[0]-1,arr[1]-1,arr[2],arr[3],arr[4],newnode2);
092        if(arr[2]>=1)
093        {
094            newnode3=(NODE)malloc(sizeof(struct buy));
095            newnode3->next=NULL;
096            copy(newnode3,1,1,1,0,0);
097            f3=3*8*0.9+foo(arr[0]-1,arr[1]-1,arr[2]-1,arr[3],arr[4],newnode3);
098            if(arr[3]>=1)
099            {
100                newnode4=(NODE)malloc(sizeof(struct buy));
101                newnode4->next=NULL;
102                copy(newnode4,1,1,1,1,0);
103                f4=4*8*0.8+foo(arr[0]-1,arr[1]-1,arr[2]-1,arr[3]-1,arr[4],newnode4);
104                if(arr[4]>=1)
105                {
106                    newnode5=(NODE)malloc(sizeof(struct buy));
107                    newnode5->next=NULL;
108                    copy(newnode5,1,1,1,1,1);
109                    f5=5*8*0.75+foo(arr[0]-1,arr[1]-1,arr[2]-1,arr[3]-1,arr[4]-1,newnode5);
110                }
111            }           
112        }
113    }
114    float res=min(min(min(f1,f2),min(f3,f4)),f5);
115    if(res==f1)
116        oldnode->next=newnode1;
117    if(res==f2)
118        oldnode->next=newnode2;
119    if(res==f3)
120        oldnode->next=newnode3;
121    if(res==f4)
122        oldnode->next=newnode4;
123    if(res==f5)
124        oldnode->next=newnode5;
125    return res;
126}

动态规划只给出最优结果,不给出最优方案是没有多大意义的,即我们还需要保留中间每一步的最优决策方案。由于每一步都有很多种可能的状态,所以这一点实现起来比较麻烦。在我的代码中用了一个结构体struct buy来保存每一步的购书组合方式。刚开始的时候我的结构体是这样定义的struct buy{struct buy* next,int *pay}*NODE; 企图用pay去指向一个五元的数组,但问题是在函数foo中给newnode的pay赋值是都只是浅复制,每次当foo退出时pay中保留的值就丢失了,导致我在main中打印最优购书组合时总是输出一些很大的随机数。就为检查出这个错误折腾了一天。C语言的指针大部分情况下还是挺好的,使用它就“自觉”地避免了大量数据的复制,但是当你想进行真正的数组复制时它却给你来“浅复制”,这次是个教训,以后注意。

原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值