算法设计与分析课程设计
算法设计与分析课程设计
课程题目
零钱问题贪心算法实现
二、课程摘要
1 )题目描述 使用贪心算法设计思想设计算法实现找零钱问题。
例题 13-4 一个小孩买了价值少于 1 美元的糖,并将 1 美元的钱交给售货员。售货员希 望用数目最少的硬币找给小孩。假设提供了数目不限的面值为
2 5 美分、 1 0 美分、5
美分、及 1 美分的硬币。 售货员分步骤组成要找的零钱数,每次加入一个硬币。选择硬 币时所采用的贪婪准则如下:每一次选择应使零钱数尽量增大。为保证解法的可行性
(即:所给的零钱等于要找的零钱数) ,所选择的硬币不应使零钱总数超过最终所需的 数目。 1) 在给定钱币面值的前提下,实现找回尽量少硬币的输出方案 2) 分析算法性能
2)贪心算法简述 在求最优解问题的过程中,
依据某种贪心标准, 从问题的初始状态出发, 直接去求每一 最终得出
整个问题的最优解, 这种求解方法就是 贪心法并不是从整体上考虑问题, 它所做出的 而由问题自身的特性决定了该题运用贪心算法可 但决不依赖于将来
步的最优解, 通过若干次的贪心选择, 贪心算法。 从贪心算法的定义可以
看出, 选择只是在某种意义上的局部最优解, 以得到最优解。 贪心算法所作的选择可以依赖于以往所作过的选择,
的选择,也不依赖于子问题的解,因此贪心算法与其它算法相比具有一定的速度优势。 如果
一个问题可以同时用几种方法解决, 贪心算法应该是最好的选择之一。 贪心算法的含义、
基本思路及实现过程, 贪心算法的核心、基本性质、 问题。 并通过贪心算法的特点举例列
出了以往研究过的几个经典问题, 的问题,也希望通过贪心算法的特点来解决。
本文讲述了
特点及其存在的
对于实际应用中
三、课程引言
首先,证明找零钱问题的贪婪算法总能产生具有最少硬币数的零钱。 证明:(1)找零钱问题的最优解必以一个贪心选择开始, 当总金额为 10 , 5, 1 时。
设最大容许的硬币面值为
m,最优解必包含一个面值为
m 的硬币:
设A 是一个最优解,且 A 中的第i 个硬币面值为f(i)。
当 f(1)=m (此处为 25)
N,硬币面值为25,
, 得证; 若f(1) A 中若不存在Ak 使f(k)=m,贝9必有n 个硬币(n>1)之和 B 的数目小于A,这与A 是最优解矛盾。 故此问题总有最优解始于贪心选择。 (2)已证明A 是最优解且A 始于贪心选择。则 A'=A-{1}是找出总额为 M-f(1)零钱 的一个最优解。若有解 B'使找零钱数少于 A',则将m 加入B'中,得到一个原问题的最 优解且优于A,这与A 是最优解矛盾。可见每步所作的贪心选择都将原问题简化为一个 规模较小的子问题,对贪心步数归纳 , 得证此问题贪心必有最优解。 四、课程正文 1) 算法设计、分析 解决找零钱问题用动态规划来解, 归结到动态规划上面就变成了无限背包问题 (因为收 银台的硬币默认是无穷的,但一种改进版本可以考察有限硬币的情况) 。区别在于,现 在我们需要求一个最少的硬币数而不是最大值。 但是选择的情况也是相同的, 择都可以选择任何一种硬币。 首先,找零钱问题具有最优子结构性质: 兑换零钱问题的最优子结构表述: 对于任意需要找的钱数 j ,一个利用 T[n] 同面值钱币进行兑换零钱的最佳方案为 P(T(1),j) , P(T(2),j),...,P(T(n),j) 时的最少钱币个数, 则 P(T(2),j),...,P(T(n),j) 币对钱数 j=j-P(T(1),j)* T(1) 进行兑换零钱的最佳方案。 其次,找零钱问题具有重叠于问题性质: a)当n=1时,即只能用一种钱币兑换零钱,钱币的面值为 C(1, j) j /T[1] (2) 答:算法的时 间复杂度主要取决于程序的两个循环,所以算法的时间复杂度为;算法 执行过程中引入了一个二维数组,随着输入规模的增大,所需要的空间复杂度为: 考虑例 1 3 - 4 的找零钱问题,假设售货员只有有限的 2 5 美分, 1 0 美分, 5 美分 和 1 美分的硬币, 给出一种找零钱的贪婪算法。 这种方法总能找出具有最少硬币数的零 钱吗证明结论。 源代码如下: # include 小孩给的钱数 const int twentyfnum=3; //25 美分硬币 即每次选 中的 n 个不 ,即此 定是利用 T[n] 中 n 个不同的面值钱 T[0] ,有 b) 当 n>1 时, 若j>T[n],即第n 种钱币面值比所兑换零钱数小 ,因此有。当k 为时,C(n,j)达到最小 值,有 P(T(k0),j)=P(T() , j-T())+1 若 j=T[n] ,即用 n 种钱币兑换零钱,第 n 种钱币面值与兑换零钱数 j 相等,此时有 C(n,j)=C(n,T[n])=1 ; 若 j 从以上讨论可知该问题具有重叠子问题性质。 (1 ) 答: 根据分析建立正确的递归关系。 C(1,j) j n-1 种钱 分析利用你的想法解决该问题可能会有怎样的时空复杂度。 美分硬币 美分硬币 美分硬币 const int tennum=3; //10 const int fivenum=3; //5 const int onenum=3; //1 const int tnum=twentyfnum+tennum+fivenum+onenum; // 硬币的总数量 int main() { int a[tnum],i; // int *p=a; for(i=0;i if(findmoney(a ,b ,tnum ,M-C)) int c[4]={0}; // 存放应找个各面值硬币的数量 bool findmoney(int *p ,bool * { for(i=0;i 数组初始化 , 数组中的元素由大到小排列 case 25: c[0]++;break; case 10: c[1]++;break; case 5: c[2]++;break; case 1: c[3]++;break; } cout<<" cout<<"25 分 :"< 找钱方案 :"< { q[i]=true; c-=p[i]; if(c==0) break; } if(c==0) return true; else return false; } 在此程序中,程序没有实现输入和输出的模块,但是具有了找零钱问题的贪心算法解决模块,所以需要在此程序的基础上进一步优化,改进后的代码如下: #include using namespace std; void Zl(double num) { int leave=0; double a[8]; leave = (int)(num*10)%10; a[1] = leave/5; a[0] = (leave%5)/1; a[7] = num/50; a[6] = ((int)num%50)/20; a[5] = (((int)num%50)%20)/10; a[4] = ((((int)num%50)%20)%10)/5; a[3] = (((((int)num%50)%20)%10)%5)/2; a[2] = ((((((int)num%50)%20)%10)%5)%2)/1; if(a[0]!=0) cout<<" if(a[1]!=0) cout<<" if(a[2]!=0) int main() { double num;需要找的元个数为: "< cout<<" 需要找的 1 元个数为:"< cout<<" 需要找的 2 元个数为:"< cout<<" 需要找的 5 元个数为:"< cout<<" 需要找的10 元个数为:"< cout<<" 需要找的20 元个数为:"<