面试中的找零问题

今天早上看一位搞cv的高手坯子的博客,看到了一个找零问题,觉得蛮有意思的,现分享如下:

问题描述:

        现存在一堆面值为 V1、V2、V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?假设这一堆面值分别为 1、2、5、21、25 元,需要找出总值 T 为 63 元的零钱。
        很明显,只要拿出 3 个 21 元的硬币就凑够了 63 元了。 

问题分析:

        找63元,如果直接找最大面值,则有25+25+5+5+2+1,明显不是最小硬币数,所以直接找最大面值不行。怎样才能找到21呢?
        将问题分解,分别找到63-1,63-2,63-5,63-21,63-25的最小硬币数,这五个中最小的,一定也就是组成63的最小硬币数。如果求62需要求61,60,57,41,37, 求61需要求60,59,56,40,36...

        用conisUsed表示每一个面值对应的最小硬币数,则: coinsUsed[i]=coinsUsed[i-coinValue]+1;
        用coinsTrack数组记录每一个面值对应的coinValue;

可以考虑使用动态规划方法:

动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,并将这些子问题的解保存起来,如果以后在求解较大子问题的时候需要用到这些子问题的解,就可以直接取出这些已经计算过的解而免去重复运算。保存子问题的解可以使用填表方式,例如保存在数组中。


具体代码如下:

#include <cstdlib>
#include <iostream>

using namespace std;


/*
  @param coinValue:
         保存硬币面值的数组
  @param coinSize
         保存面值的种类,即coniValue的大小
  @param moneyValve
         需要找零的面值
  @param coinsUsed
         记录每一个面值所对应的最小硬币数
  @param coinsTrack
         记录组成每一个面值所对应的,使其硬币数最小所加入的硬币面值 
*/ 
void MakeChange(int* coinValue,size_t coinSize,int moneyValue,int *coinsUsed,int *coinsTrack)
{
     
     //给coinsUsed、coinsTrack赋初值
     coinsUsed[0]=0;
     coinsTrack[0]=0;
      
     //对moneyValue以下的所有money面值都要计算,都要求解以备用 ,cent为当前面值 
     for(int cent=1;cent<=moneyValue;cent++)
     {
         //记录每种money面值所对应的子问题的最小值,minCons初值为全部为1的硬币数 
         int minCoins=cent;
         //记录每种money面值所对应的加入的硬币面值          
         int lastIn=0;
         
         //分别对每种coin面值,求解其子问题
         for(int j=0;j<coinSize;j++) 
         {
             //如果当前面值大于等于硬币面值,则继续;否则退出 
             if(cent>=coinValue[j])
             {
                 //找到最小的子问题解 
                 if(coinsUsed[cent-coinValue[j]]<minCoins)
                 {
                     minCoins=coinsUsed[cent-coinValue[j]];
                     lastIn=coinValue[j];
                 }
             }
         }
         
         //保存最小硬币数 
         coinsUsed[cent]=minCoins+1;
         coinsTrack[cent]=lastIn;
     }
     
     //输出结果
     cout<<"面值为"<<moneyValue<<"所需要的最小硬币为:"<<coinsUsed[moneyValue];
     cout<<" 其面值分别为:";
     while(moneyValue>0)
     {
         cout<<coinsTrack[moneyValue]<<" ";
         moneyValue-=coinsTrack[moneyValue];
     }
} 

int main(int argc, char *argv[])
{
    //硬币面值 
    int value[]={1,2,5,21,25};
    //硬币数组大小 
    size_t size=sizeof(value)/sizeof(*value);
    //要找零的数 
    int money=45;
    //保存每个面值对应最小值,因为0号位置舍弃,故加1 
    int* coinsUsed=new int[money+1];
    int* coinsTrack=new int[money+1];
    
    MakeChange(value,size,money,coinsUsed,coinsTrack);
    
    system("PAUSE");
    return EXIT_SUCCESS;
}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值