zoukankan      html  css  js  c++  java
  • 理解动态规划算法与贪心算法区别----找钱问题

    一、说明

          纠结了很久动态规划法与贪心算法的异同,光看理论文字的说明,没有能彻底搞清楚两者的区别究竟是什么。发现大家举得最多的一个

    比较两者区别例子就是找钱问题。解决这个找钱问题,可以很大程度上帮助我们理解动态规划法语贪心算法的区别

    二、问题

          现只有面额为 11元、5元、1元的三种人民币。

          给定一个 数目为 money 的人民币,如何用这三种面额的人民币 找开它,且用的人民币张数最少

          如:给定 10元,我们可以有以下找法:

                2张  5元面额

                1张  5元面额  + 5 张  1元面额

                10张 1元面额

          我们 选择第一种找法。只用两张人民币。

    三、分析

    利用动态规划法可以找到最优解。

            利用贪心算法可以找到最优解(问题满足贪心选择性质时。该找钱问题在 11、5、1三种面额的情况下不满足该性质)

                  或者找到近似 最优解(在本题设定的三种面额的情况下 便是如此)

            如果现在要找开 15元钱,则

            1. 根据动态规划法的解题思想,得到最优解为       3张  5元面额的 ,                                   总共 3张

            2. 根据贪心算法的解题思想,得到的近似最优解为 1张 11元面额的  加上  4张 1元面额的,     总共 5张

            从这就可以大略的看到 两个的区别

    四、代码实现找钱问题的 动态规划法与贪心算法 两种解法,形成对比

    [cpp] view plain copy
    1. /********************************************************** 
    2.  *问  题:有最小面额为 11 5 1的三种人民币,用最少的张数找钱 
    3.  *描  述:动态规划与贪心算法 解决问题 比较 
    4.  *作  者:JarvisChu 
    5.  **********************************************************/  
    6. #include<stdio.h>  
    7. #define N 4     
    8. #define VALUE1 11                  //面额为 11元的人民币 (可以修改)  
    9. #define VALUE2 5                   //面额为  5元的人民币 (可以修改)  
    10. #define VALUE3 1                   //面额为  1元的人民币 (不要修改,不然会有找不开的情况)  
    11. #define MAX_MONEY 1000             //能找开的钱的上限  
    12.   
    13. /***************************动态规划法******************************** 
    14.  *方法: 
    15.  *     int Num[MAX_MONEY];                  //Num[i]保存要找开 i 元钱,需要的最小人民币张数 
    16.  *     int Num_Value[N][MAX_MONEY];         //Num_Value[i][j] 表示 要找 j元钱,需要面额 VALUEi 的人民币张数 
    17.  * 
    18.  *     Num[i] = i;          0<= i <=4 
    19.  *     Num[i] = min(Num[i-VALUE1]+1,Num[i-VALUE2]+1,Num[i-VALUE3]+1) 
    20.  */  
    21.   
    22. //-------------------------求最小值---------------------------------  
    23. int min(int a,int b,int c){  
    24.     return a<b ? (a<c ? a:c):(b<c ? b:c);  
    25. }  
    26. //-------------------------求最优值---------------------------------  
    27. int DP_Money(int money,int Num[]){  
    28.                                                          //获得要找开money元钱,需要的人民币总张数             
    29.     int i;  
    30.     for(i=0;i<=VALUE2;i++){                               //0~4 全用 1元   
    31.         Num[i]=i;  
    32.     }  
    33.     for(i=VALUE2;i<=money;i++){                           //从5元开始 凑钱  
    34.         if(i-VALUE1 >= 0){                                //如果比 11 元大,说明多了一种用11元面额人民币的可能  
    35.                                                          //从用 11元、5元、1元中 选择一个张数小的  
    36.             Num[i] = min(Num[i-VALUE1]+1,Num[i-VALUE2]+1,Num[i-VALUE3]+1);  
    37.         }              
    38.         else{                                            //从5元、1元中 选择一个张数小的  
    39.             Num[i] = (Num[i-VALUE2]+1) < (Num[i-VALUE3]+1) ? (Num[i-VALUE2]+1):(Num[i-VALUE3]+1);  
    40. //          Num[i] = min(Num[i-VALUE2]+2,Num[i-VALUE2]+1,Num[i-VALUE3]+1);   
    41.         }  
    42.     }  
    43.     return Num[money];  
    44. }  
    45. //-------------------------求最优解---------------------------------  
    46. void BestChoice(int money,int Num[],int Num_Value[N][MAX_MONEY]){  
    47.                                                            //要找 money 元钱,总人民币张数放在Num[money]中  
    48.                                                            //Num[1~3][money]分别保存三种面额的张数  
    49.     int i;                                                
    50.     for(i=0;i<VALUE2;i++){                           
    51.         Num_Value[1][i] = 0;  
    52.         Num_Value[2][i] = 0;  
    53.         Num_Value[3][i] = i;  
    54.     }  
    55.     for(i=VALUE2;i<=money;i++){  
    56.         if((i>=VALUE1) && (Num[i] == (Num[i-VALUE1]+1))){   //i 是由 i-11+11 构成  即i元是由 i-11元 加上一张面额11元的人民币构成  
    57.             Num_Value[1][i] = Num_Value[1][i-VALUE1]+1;     //多一张 11元面额人民币  
    58.             Num_Value[2][i] = Num_Value[2][i-VALUE1];       // 5元面额人民币 张数一样多  
    59.             Num_Value[3][i] = Num_Value[3][i-VALUE1];       // 1元面额人民币 张数一样多  
    60.         }  
    61.         else if(Num[i] == (Num[i-VALUE2]+1)){               //i 是由 i-5+5 构成           
    62.             Num_Value[1][i] = Num_Value[1][i-VALUE2];       //11元面额人民币 张数一样多  
    63.             Num_Value[2][i] = Num_Value[2][i-VALUE2]+1;     //多一张 5元面额人民币  
    64.             Num_Value[3][i] = Num_Value[3][i-VALUE2];       // 1元面额人民币 张数一样多  
    65.         }  
    66.         else if(Num[i] == (Num[i-VALUE3]+1)){               //i 是由 i-1+1 构成     
    67.             Num_Value[1][i] = Num_Value[1][i-VALUE3];       //11元面额人民币 张数一样多  
    68.             Num_Value[2][i] = Num_Value[2][i-VALUE3];       // 5元面额人民币 张数一样多  
    69.             Num_Value[3][i] = Num_Value[3][i-VALUE3]+1;     //多一张 1元面额人民币  
    70.         }  
    71.         else{  
    72.         }  
    73.     }  
    74. }  
    75.   
    76. /***************************贪心算法******************************** 
    77.  *方法: 
    78.  *     Num_Value[i]表示 面额为VALUEi 的人民币用的张数 
    79.  *     能用大面额的人民币,就尽量用大面额 
    80.  */  
    81. int Greed(int money,int Num_Value[]){  
    82.                                                             //要找开 money元人民币,Num_Value[1~3]保存 三种面额人民币的张数  
    83.     int total=0;                                            //总张数,返回值也即是总张数。  
    84.     Num_Value[1] = 0;  
    85.     Num_Value[2] = 0;  
    86.     Num_Value[3] = 0;  
    87.     for(int i=money;i>=1;){  
    88.         if(i >= VALUE1){  
    89.             Num_Value[1]++;  
    90.             i -= VALUE1;  
    91.             total++;  
    92.         }  
    93.         else if(i >= VALUE2){  
    94.             Num_Value[2]++;  
    95.             i -= VALUE2;  
    96.             total++;  
    97.         }  
    98.         else if(i >= VALUE3){  
    99.             Num_Value[3]++;  
    100.             i -= VALUE3;  
    101.             total++;  
    102.         }  
    103.         else{  
    104.         }  
    105.     }  
    106.     return total;  
    107. }  
    108. void main(){  
    109.     //<a href="http://lib.csdn.net/base/softwaretest" class='replace_word' title="软件测试知识库" target='_blank' style='color:#df3434; font-weight:bold;'>测试</a> 动态规划法  
    110. /*  int i; 
    111.     int money = 23; 
    112.     int Num[MAX_MONEY];                  //Num[i]保存要找开 i 元钱,需要的最小人民币张数 
    113.     int Num_Value[N][MAX_MONEY];         //Num_Value[i][j] 表示 要找 j元钱,需要面额 VALUEi 的人民币张数 
    114.     printf("%d/n",DP_Money(money,Num)); 
    115.     printf("-------------------------------------------/n"); 
    116.     BestChoice(money,Num,Num_Value); 
    117.     printf("-------------------------------------------/n"); 
    118.     for(i=0;i<=money;i++){ 
    119.         printf("Num[%d]=%4d, %3d, %3d, %3d/n",i,Num[i],Num_Value[1][i],Num_Value[2][i],Num_Value[3][i]); 
    120.     } 
    121. */  
    122.   
    123.     //测试 贪心算法  
    124. /*  int i; 
    125.     int Num_Value_Greed[4]; 
    126.     for(i=0;i<=40;i++){                 //从0 元到 40 元的每一个找钱方式 
    127.         Greed(i,Num_Value_Greed); 
    128.         printf("%d---->>> %d,%d,%d/n",i,Num_Value_Greed[1],Num_Value_Greed[2],Num_Value_Greed[3]); 
    129.     } 
    130. */  
    131.       
    132.     //比较两个算法  
    133.     int i;  
    134.     int dp,grd;                         //分别保存动态规划法和贪心算法得到的人民币总张数  
    135.     int money;                          //要找的钱  
    136.     int Num[MAX_MONEY];                 //Num[i]保存要找i花费的银币的数目  
    137.     int Num_Value[N][MAX_MONEY];        //Num_Value[i][j] 表示 要找 j 花费的 面值为 VALUEi 的硬币 的数目  
    138.     int Num_Value_Greed[N];             //Num_Value_Greed[i] 表示 面值为VALUEi 的人民币 数目   
    139.     money = 15;                         //可以为任意非负整型值(15 元是一个比较典型的可以区分两种算法的值)  
    140.     dp = DP_Money(money,Num);          //动态规划法  
    141.     BestChoice(money,Num,Num_Value);  
    142.     grd = Greed(money,Num_Value_Greed); //贪心算法  
    143.     printf("要找的钱 为:%d/n/n",money);  
    144.     printf("  算法    张数  11元  5元  1元/n");  
    145.     printf("动态规划  %-4d  %-4d  %-3d  %-3d/n",dp,Num_Value[1][money],Num_Value[2][money],Num_Value[3][money]);  
    146.     printf("贪心算法  %-4d  %-4d  %-3d  %-3d/n",grd,Num_Value_Greed[1],Num_Value_Greed[2],Num_Value_Greed[3]);  
    147. }  
  • 相关阅读:
    ubuntu frp 自编译。本文不能按顺序来 请自己理解
    油猴子 自改脚本 删除页面 div 上下翻页 视频页内全屏 右键可用
    批处理bat 删除指定文件夹下的文件及文件夹
    LUA 静态库 动态库 LD_LIBRARY_PATH 动态库的查找路径 GCC “-l”参数
    delphi 判断奇数偶数
    sf.net
    cmake指定mingw编译器的方法
    关闭delphi ide皮肤
    arch pacman被删除 重装
    delphi 匿名方法访问var参数
  • 原文地址:https://www.cnblogs.com/aabbcc/p/6504557.html
Copyright © 2011-2022 走看看