算法设计与分析课程设计

算法设计与分析课程设计

课程题目

零钱问题贪心算法实现

二、课程摘要

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 using namespace std; const int C=33; const int M=100; //

小孩给的钱数

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 元个数为:"<

cout<<" 需要找的50 元个数为:"<

需要找的元个数为: "<

cout<<" 请输入你需要找的零钱数: "<>num; Zl (num ); cout<

}

2)实验结果比较 上一步骤中两个源代码运行结果分别如下: 第一个的运行结果

第二个运行结果

比较上面两个算法, 第二个算法在第一个的基础上增加了输入输出功能, 值零钱问题的最优解。

五、结论与展望

(1) 算法实现的复杂度在问题规模很大时可以接受吗 答:可以接受, 因为贪心算法有很好的效率, 所以当问题复杂度很大时,就不会对算法的运 行时间有太大的影响,可以控制在用户可以接受的范围内。 (2) 如果不用贪心算法方法还能想到其他的解决方式吗和贪心算法相比会有更好的效 率吗

答:对于找硬币问题,有时候动态规划也能解决, 前面也有叙述, 动态规划求解要比贪心算 法求解有效率,所以采用动态规划方法也是一个很好的选择。 (3)

所选用的数据结构合适吗 答:采用了数组的数据结构, 合适, 因为该数据结构能够支持对于

数组中的元素的随机访问, 而且方便查询。 (4)

该算法都存在哪几类可能出现的情况,你的测试完全覆盖了你所想到的这些情况 吗,测试结果

如何 答:基本上覆盖了所有可能的测试结果,但不排除结果出现错误的可能。

(5)通过实验对贪心算法的理解及总结 *贪心算法的特点 从全局来看, 运用贪心策略解决的问题在程序的运行过程中无回溯过程, 后面的每一步都是 当前看似最佳的选择,这种选择依赖于已做出的选择,但不依赖于未做出的选择。

* 贪心算法存在的问题 ①不能保证求得的最后解是最佳的。 由于贪心策略总是采用从局部看来是最优的选择, 并不从整体上加以考虑。

②贪心算法只能用来求某些最大或最小解的问题。 例如找零钱问题要求得到最小数量, 以采用贪心算法,但是在另外一个求解权值最小路径时采用贪心算法得到的结果并不是最 佳。

方便得到任意数

因此 就可

相关文档
最新文档