动态规划解找零钱问题实验报告

合集下载

动态规划建模实验报告

动态规划建模实验报告

一、实验背景动态规划是一种重要的算法设计方法,它通过将复杂问题分解为若干个相互重叠的子问题,并存储子问题的解,从而避免重复计算,有效地解决一系列优化问题。

本实验旨在通过具体案例,加深对动态规划算法的理解和应用。

二、实验目的1. 掌握动态规划的基本概念和原理。

2. 熟悉动态规划建模的过程和步骤。

3. 提高运用动态规划解决实际问题的能力。

三、实验内容本次实验选取了“背包问题”作为案例,旨在通过解决背包问题,加深对动态规划算法的理解。

四、实验步骤1. 问题分析背包问题是一个经典的组合优化问题,描述为:给定一个容量为C的背包和N件物品,每件物品有价值和重量两个属性,求如何将物品装入背包,使得背包中的物品总价值最大,且不超过背包的容量。

2. 模型建立(1)定义状态:设dp[i][j]表示在前i件物品中选择若干件装入容量为j的背包所能获得的最大价值。

(2)状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i]] + values[i]),其中weights[i]表示第i件物品的重量,values[i]表示第i件物品的价值。

(3)边界条件:dp[0][j] = 0,表示没有物品时,背包价值为0。

3. 编程实现使用C语言编写动态规划程序,实现背包问题的求解。

4. 结果分析(1)运行程序,输入背包容量和物品信息。

(2)观察输出结果,包括物品选择的列表和最大价值。

(3)验证结果是否正确,与理论分析进行对比。

五、实验结果与分析1. 实验结果:通过编程实现,成功求解了背包问题,并得到了最大价值。

2. 结果分析:(1)动态规划算法在解决背包问题时,有效地避免了重复计算,提高了求解效率。

(2)实验结果表明,动态规划算法能够有效地解决背包问题,为实际应用提供了有力支持。

六、实验总结1. 动态规划是一种重要的算法设计方法,具有广泛的应用前景。

2. 动态规划建模过程中,关键在于正确地定义状态和状态转移方程。

动态规划算法实验报告

动态规划算法实验报告

南京信息工程大学滨江学院实验(实习)报告1.实验目的动态规划通常用来求解最优化问题。

通过本次实验掌握动态规划算法。

通过矩阵连乘问题和0-1背包问题实现动态规划算法。

学会刻画问题的最优结构特征,并利用最优化问题具有的重叠子问题性质,对每个子问题求解一次,将解存入表中,当再次需要这个子问题时直接查表,每次查表的代价为常量时间。

2.实验内容及分析设计过程1.矩阵链乘法问题矩阵链乘法问题可描述如下:给定个矩阵的链,矩阵的规模为,求完全括号方案,使得计算乘积所需的标量乘法次数最少。

令m[i,j]表示计算矩阵所需标量乘法次数的最小值,那么,原问题的最优解计是m[1,n]。

最小代价括号化方案的递归求解公式为采用自底向上表格法代替上述递归算法来计算最优代价。

为了实现自底向上方法,我们必须确定计算m[i,j]时需要访问哪些其他表项。

上述公式显示,j-i+l 个矩阵链相乘的最优计算代价m[i,j] 只依赖于那些少于j-i+l 个矩阵链相乘的最优计算代价。

因此,算法应该按长度递增的顺序求解矩阵链括号化问题,并按对应的顺序填写表m。

对如下输入A1 A2 A3 A4 A5 A630⨯35 35⨯15 15⨯5 5⨯10 10⨯20 20⨯25程序运行结果为2.背包问题给定n 个重量为价值为的物品和一个承重为W 的背包。

求这些物品中最有价值的一个子集,并且要能装到背包中。

设V[i,j]是能够放进承重量为j 的背包的前i 个物品中最有价值子集的总价值。

则递推关系为初始条件V[0,j]=0(j>=0),V[i,0]=0(i>=0) 我们的目标是求V[n ,W]。

递归式给出了V[i,j]的计算顺序,V[i,j]只依赖与前一行的那些项。

故可以逐行计算V[i,j].对于物品数量n=5,w[n]={2,2,6,5,4},v[n]={6,3,5,4,6},背包总重量c=10 程序运行结果为3. 实验小结通过本次实验加深了我对动态规划算法的理解。

动态规划解找零钱问题实验报告

动态规划解找零钱问题实验报告
2 3 的情况下需要执行 O(M n ) O(n ) ,而 M 小于 100 元即 10000 分,远
大于 n。本算法的动态规划算法的时间复杂性比该问题的一般动态规划 算法的效率要好得多。 该算法的时间复杂性是 10 数量级的.对于应用于 自动售货机等运行速度较慢的机器来说是不成问题的。 空间复杂度:从上面算法可知,用到了三个数组,分别为 T[n],c[j], P[i][j]。其中:i<=n,j<=M。空间复杂性主要由 P[1][j]决定,为 O(M×n)。 P(i,j)中的 i 指的 T[n]中的值.对于钱币来说一般 n 为 13 左右。该算法的 空间复杂度为 O(M x n)=O(f),而 M 小于 100 元即 10 000 分,远大于 n。

b)当 n>1 时, 若 j>T[n],即第 n 种钱币面值比所兑换零钱数小,因此有 C (n, j ) min {C (n, j T [k ]) 1} k (1 i n) 1 k n 。当 k 为 0 时,C(n,j)达到最小 值,有 P(T(k0),j)=P(T( k 0 ),j-T( k 0 ))+1 若 j=T[n],即用 n 种钱币兑换零钱,第 n 种钱币面值与兑换零钱数 j 相等,此时有 C(n,j)=C(n,T[n])=1;
P (i , j ) P (i , T [ n ])

1,i T [ n ] 0 ,i T [ n ]
若 j<T[n],即第 n 种钱币面值比所兑换零钱数大,因此兑换零钱只 需考虑前 n-1 种钱币即可,故有 C(n,j)=C(n-1,j),且 P(T(n-1),j)=0。 从以上讨论可知该问题具有重叠子问题性质。 (2) 根据分析建立正确的递归关系。 答: j % T [1] 0 C (1, j ) j / T [1] j % T [1] 0

贪心算法-找零问题 实验报告

贪心算法-找零问题 实验报告

实验三课程名称:算法设计与实现实验名称:贪心算法-找零问题实验日期:2019年5月2日仪器编号:007班级:数媒0000班姓名:郝仁学号0000000000实验内容假设零钱系统的币值是{1,p,p^2,……,p^n},p>1,且每个钱币的重量都等于1,设计一个最坏情况下时间复杂度最低的算法,使得对任何钱数y,该算法得到的零钱个数最少,说明算法的主要设计思想,证明它的正确性,并给出最坏情况下的时间复杂度。

实验分析引理1(离散数学其及应用3.1.4):若n是正整数,则用25美分、10美分、5美分和1美分等尽可能少的硬币找出的n美分零钱中,至多有2个10美分、至多有1个5美分、至多有4个1美分硬币,而不能有2个10美分和1个5美分硬币。

用10美分、5美分和1美分硬币找出的零钱不能超过24美分。

证明如果有超过规定数目的各种类型的硬币,就可以用等值的数目更少的硬币来替换。

注意,如果有3个10美分硬币,就可以换成1个25美分和1个5美分硬币;如果有2个5美分硬币,就可以换成1个10美分硬币;如果有5个1美分硬币,就可以换成1个5美分硬币;如果有2个10美分和1个5美分硬币,就可以换成1个25美分硬币。

由于至多可以有2个10美分、1个5美分和4个1美分硬币,而不能有2个10美分和1个5美分硬币,所以当用尽可能少的硬币找n美分零钱时,24美分就是用10美分、5美分和1美分硬币能找出的最大值。

假设存在正整数n,使得有办法将25美分、10美分、5美分和1美分硬币用少于贪心算法所求出的硬币去找n美分零钱。

首先注意,在这种找n美分零钱的最优方式中使用25美分硬币的个数q′,一定等于贪心算法所用25美分硬币的个数。

为说明这一点,注意贪心算法使用尽可能多的25美分硬币,所以q′≤q。

但是q′也不能小于q。

假如q′小于q,需要在这种最优方式中用10美分、5美分和1美分硬币至少找出25美分零钱。

而根据引理1,这是不可能的。

动态规划实验报告

动态规划实验报告

实验课程:算法分析与设计实验名称:实验3 动态规划算法(综合性/设计性)实验目标:1、熟悉最长公共子序列问题的算法;2、初步掌握动态规划算法;实验任务:若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。

例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。

给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X 和Y的公共子序列。

给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

实验设备及环境:PC;C/C++的编程环境Visual C++。

实验主要步骤:(1)明确实验目标和具体任务;(2)理解实验所涉及的动态规划算法;(3)编写程序并实现动态规划算法;(4)设计实验数据并运行程序、记录运行的结果;实验数据及运行结果、实验结果分析及结论:(学生填写)#include <stdio.h>#include <string.h>void LcsLength(char *x,char *y,int m,int n,int c[][100],int b[][100]){puts(x);puts(y);int i,j;for(i=0;i<=m;i++)c[i][0]=0;for(j=1;i<=n;j++)c[0][j]=0;for(i=1;i<=m;i++)for(j=1;j<=n;j++) {if(x[i-1]==y[j-1]) {c[i][j]=c[i-1][j-1]+1;b[i][j]=0;}else if(c[i-1][j]>=c[i][j-1]) {c[i][j]=c[i-1][j];b[i][j]=1;}else {c[i][j]=c[i][j-1]; b[i][j]=-1;}}}void PrintLCS(int b[][100], char *x, int i, int j){ if(i==0 || j==0)return;if(b[i][j]==0) {PrintLCS(b,x,i-1,j-1);printf("%c",x[i-1]);}else if(b[i][j]==1)PrintLCS(b,x,i-1,j);elsePrintLCS(b,x,i,j-1);}void main(){char x[100]={"ABCBDAB"};char y[100]={"BDCABA"};int c[100][100];int b[100][100];int m,n;m=strlen(x);n=strlen(y);LcsLength(x,y,m,n,c,b); printf("最长子序列为:");PrintLCS(b,x,m,n); printf("\n");printf("最长子序列长度为:%d\n",c[m][n]);}实验结果:结果分析:在写规划方程时,只要对两条路径走到同一个点的情况稍微处理一下,减少可选的决策个数:从这个例子中可以总结出设计动态规划算法的一个技巧:状态转移一般。

《动态规划算法实验》实验报告

《动态规划算法实验》实验报告

实验3、《动态规划算法实验》一、实验目的1. 掌握动态规划方法贪心算法思想2. 掌握最优子结构原理3. 了解动态规划一般问题二、实验内容1. 编写一个简单的程序,解决0-1背包问题。

设N=5,C=10,w={2,2,6,5,4},v={6,3,5,4,6}2. 合唱队形安排问题【问题描述】N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K 位同学排成合唱队形。

合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。

已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

三、算法思想分析1.0-1背包采用动规算法主要是动规方程的思考,之后就是确定边界条件即可。

2.合唱队形问题应用了分治与动态规划的算法,先将所有队员依次做中间最高的同学,将问题分为左右来做,接下来只需要求得左边的最长上升子序列数、右边的最长下降子序列数即可。

四、实验过程分析1.0-1背包问题是背包问题的进一步条件限制,考虑清楚动规方程就不难,编程中对于m(i,j)的含义要清楚,搞混了就容易出错。

2.合唱队形问题的思想并不复杂,特别是如果已经掌握了最长上升子序列数的算法,在分别处理左右最长子序列时需要特别注意数组下标,一开始我用是i,j直接从0到左右的数据长度,但是一直出错,后来发现队员身高数组并不能完全用这些下标,特别是右边的函数,数组起始下标不是0,需要利用函数传递起始下标才能调用对应的数据段。

五、算法源代码及用户屏幕1.(1)算法源码/********************************0-1背包问题。

codeblocks C++2018.11.2********************************/#include <iostream>#include <iomanip>using namespace std;void knapSnack(int v[], int w[], int c, int n, int m[][11]);int main(){int v[] = {6, 3, 5, 4, 6};int w[] = {2, 2 ,6, 5, 4};int c = 10;int n = 5;int m[5][11];//初始化数组for(int i=0; i<5; i++){for(int j=0; j<11; j++){m[i][j] = 0;}}knapSnack(v, w, c, n, m);//输出结果cout<<setw(3)<<" ";for(int i=0; i<11; i++){cout<<setw(3)<<i;}cout<<endl;for(int i=0; i<5; i++){//输出行号cout<<setw(3)<<i+1;for(int j=0; j<11; j++){cout<<setw(3)<<m[i][j];}cout<<endl;}return 0;}void knapSnack(int v[], int w[], int c, int n, int m[][11]){ for(int i=0; i<n; i++){for(int j=0; j<11; j++){//边界条件if(i == 0){if(w[i] > j)m[i][j] = 0;elsem[i][j] = v[i];}/*动规方程j>w[i]m(i,j) = max{m(i-1,j), m(i-1,j-w[i])+v[i]}0<=j<w[i]m(i,j) = m(i-1,j)*/else{if(w[i] > j)m[i][j] = m[i-1][j];else{if(m[i-1][j] > (m[i-1][j-w[i]]+v[i]))m[i][j] = m[i-1][j];elsem[i][j] = m[i-1][j-w[i]]+v[i];}}}//控制列数的for循环}//控制行数的for循环}(2)用户屏幕2.(1)算法源码/***************************************************合唱队形问题codeblocks C++2018.11.2***************************************************/#include <iostream>#include <string.h>using namespace std;//计算左端合唱队人数int leftQueue(int a[], int _start, int _end);//计算右端合唱队人数int rightQueue(int a[], int _start2, int _end2);int main(){cout<<"Please enter total number:";int number;cin>>number;cout<<"Please input the height of each person (cm):"<<endl;int a[number]; //记录每个人身高//b数组分别记录当第n个人为合唱队中间人时,合唱队的总人数int b[number];int rightNumber[number]; //记录左端合唱队人数int leftNumber[number]; //记录右端合唱队人数for(int i=0; i<number; i++)b[i] = 0;for(int i=0; i<number; i++)cin>>a[i];int mostQueueNumber = b[0];for(int i=0; i<number; i++){//设置a[i]为最高的同学leftNumber[i] = leftQueue(a,0,i);rightNumber[i] = rightQueue(a,i,number-1);//计算合唱队总人数b[i] = leftNumber[i] + rightNumber[i] - 1;//计算合唱队最多的总人数if(mostQueueNumber < b[i])mostQueueNumber = b[i];}//计算最少出队人数int leastDequeueNumber = number - mostQueueNumber;cout<<"Minimum number of people out: "<<leastDequeueNumber<<endl;return 0;}int leftQueue(int a[], int _start, int _end){int leftMostNumber = 0;int n = _end-_start+1;//c数组记录i时的最长上升子序列数int c[n];int maxN;//初始化最长上升子序列数为1for(int i=0; i<n; i++){c[i] = 1;}for(int i=_start; i<_end+1; i++){maxN = 0;for(int j=i-1; j>=_start; j--){if(a[j]<a[i] && c[j]>maxN)maxN = c[j];c[i] = maxN + 1;}}leftMostNumber = c[n-1];return leftMostNumber;}int rightQueue(int a[], int _start2, int _end2){ int rightMostNumber = 0;int n2 = _end2-_start2+1;//c2数组记录i时的最长下降子序列数int c2[n2];int maxN2;//初始化最长下降子序列数为1for(int i=0; i<n2; i++){c2[i] = 1;}for(int i=_end2; i>=_start2; i--){maxN2 = 0;for(int j=i+1; j<=_end2; j++){if(a[j]<a[i] && c2[j-_start2]>maxN2)maxN2 = c2[j-_start2];c2[i-_start2] = maxN2 + 1;}}rightMostNumber = c2[0];return rightMostNumber; }(2)用户屏幕。

找零钱问题算法报告

找零钱问题算法报告

《找零钱》实验报告目录一. 问题描述1. 问题描述 (2)二.算法描述与分析1. 找零钱问题算法伪代码 (3)2. 算法分析 (5)三.实验结果与分析1. 实验环境 (5)2. 实验的执行 (5)3.实验结果 (6)4.找零钱的其他情况 (6)四.总结与展望1. 总结 (8)2. 展望 (8)3.任务分工 (8)五.代码1. 贪婪技术 (8)2. 动态规划 (10)1问题描述一个小孩买了价值少于1美元的糖,假设需要找给小孩n美分。

不过他的钱包里只有1美元,于是他将1美元的钱交给售货员。

收银台有数目不限的硬币,面值分别为25美分、10美分、5美分、及1美分,如果售货员希望用数目最少的硬币找给小孩,要求设计算法,使得售货员以最少的硬币数,用25、10、5、1美分凑齐n美分。

(n<100)要求通过该问题,掌握贪心算法和动态规划算法的具体实现步骤,理解算法的基本思想,加深巩固对解决问题的思路和方法。

其中,考虑到需要解决问题的方法具有普遍适用性,该题着重动态规划算法的实现。

找零钱问题是动态规划经典题目之一。

该问题的求解方法,可类比于背包问题(The Knapsack Problem)的求解。

图1 动态规划2算法描述与分析假设零钱的面额为v1, v2,..., v m(面额升序排列),需要给出w元的找零,使用各面额的零钱的数量为n1,n2,...,n m.贪心技术解决找零钱问题的思想:想要找补的零钱数量最少,肯定优先使用面额大的零钱。

(1)将零钱按照面额大小排序;(2)总是尝试用当前面额最大的零钱来找补,在不超过需要找补的总额的条件下,尽可能的多用当前面额最大的零钱,并计算出剩余的需要找补的总额;(3)没有达到需要找补的总额的情况下,重复步骤(2)直到达到需要找补的总额。

贪心技术解决找零钱问题的正确性证明:使用贪心技术解决找零钱问题得到最优解对零钱的面额是有要求的,对于零钱面额为c 0, c 1,..., c m (其中c >1,m ≥1)的找零问题,使用贪心技术是可以得到最优解的,证明如下:假设需要找零w 元,对于此问题未使用贪心技术得到的一个最优解S ,使用面额为c i 的零钱数量为n i ,则n i ≤c −1;如果n i ≥c ,则可以使用面值为c i+1的零钱找补,使所用的零钱数量比最优解少,出现矛盾。

动态规划求解零钱兑换问题-算法课程设计

动态规划求解零钱兑换问题-算法课程设计

算法设计与分析课程设计动态规划求解零钱兑换问题目录1.绪论 (1)2.需求分析 (2)3.总体设计 (2)3.1代码设计思路 (3)3.1.1数据结构设计 (3)3.1.2动态规划过程 (3)3.1.3代码实现 (3)3.2本文求解的问题 (4)3.3流程图 (5)4.详细设计 (6)4.1对比贪心算法的不足之处 (6)4.2零钱兑换问题类比为完全背包问题 (6)4.3伪代码实现 (6)4.4Java进行代码实现 (8)4.4.1数据结构设计 (8)4.4.2动态规划过程 (8)4.4.3异常处理 (8)4.4.4实现代码 (9)5.调试分析 (10)5.1测试数据 (10)5.2测试输出结果截图 (10)5.3复杂度分析 (13)5.3.1时间复杂度 (13)5.3.2空间复杂度 (14)6.结论 (15)6.1预期目标 (15)6.2是否达到目标 (15)6.3改进措施 (15)6.4使用到的算法 (15)心得体会 (17)参考文献 (18)附录 (19)1.绪论零钱兑换问题是一个经典的计算机科学优化问题,它涉及到通过最小数量的硬币组合来达到特定金额的问题。

这个场景在生活中十分常见,例如自动售货机的找赎系统,银行的货币分配系统等。

然而,随着货币种类和面值的增加,该问题变得越来越复杂,因此寻找一个既有效又高效的方式来解决这个问题成为了当务之急。

过去数十年,众多学者已经对此问题进行了深入研究,并尝试了许多方法,如贪婪算法和分治法等。

尽管这些方法已经提供了一定程度的解决方案,但在面对大量硬币种类和大额度时,常常无法找到全局最优解。

因此,需要寻找更高效且精确的解决问题的方式。

动态规划简介动态规划是一种用于求解具有重叠子问题和最优子结构问题的最优化方法。

通过建立状态转移方程,动态规划能够避免重复计算,有效地降低时间复杂度。

另外,动态规划具有能够找出全局最优解的优点,可以找到问题的最佳答案。

动态规划在零钱兑换问题中的潜在优势由于零钱兑换问题拥有明显的重叠子问题和最优子结构属性,因此适合采用动态规划来解决。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一、实验目的(1)熟练掌握动态规划思想及教材中相关经典算法。

(2)掌握用动态规划解题的基本步骤,能够用动态规划解决一些问题。

二、实验内容与实验步骤(1)仔细阅读备选实验的题目,选择一个(可选多个)作为此次实验题目,设计的程序要满足正确性,代码中有关键的注释,书写格式清晰,简洁易懂,效率较高,利用C++的模板,设计的程序通用性好,适合各种合理输入,并能对不合理输入做出正确的提示。

(2)可供选择的题目有以下2个:(i)找零钱问题(难度系数为3)★问题描述设有n种不同面值的硬币,各硬币的面值存于数组T[1:n]中。

现要用这些面值的硬币来找钱,可以实用的各种面值的硬币个数不限。

当只用硬币面值T[1],T[2],…,T[i]时,可找出钱数j的最少硬币个数记为C(i,j)。

若只用这些硬币面值,找不出钱数j时,记C(i,j)=∞。

★编程任务设计一个动态规划算法,对1≤j≤L,计算出所有的C( n,j )。

算法中只允许实用一个长度为L的数组。

用L和n作为变量来表示算法的计算时间复杂性★数据输入由文件input.txt提供输入数据。

文件的第1行中有1个正整数n (n<=13),表示有n种硬币可选。

接下来的一行是每种硬币的面值。

由用户输入待找钱数j。

★结果输出程序运行结束时,将计算出的所需最少硬币个数输出到文件output.txt中。

输入文件示例输出文件示例input.txt output.txt331 2 59三、实验环境四、问题分析(1) 分析要解决的问题,给出你的思路,可以借助图表等辅助表达。

答:这个问题用动态规划来解,归结到动态规划上面就变成了无限背包问题(因为收银台的硬币默认是无穷的,但一种改进版本可以考察有限硬币的情况)。

区别在于,现在我们需要求一个最少的硬币数而不是最大值。

但是选择的情况也是相同的,即每次选择都可以选择任何一种硬币。

首先,找零钱问题具有最优子结构性质:兑换零钱问题的最优子结构表述:对于任意需要找的钱数j ,一个利用T[n]中的n 个不同面值钱币进行兑换零钱的最佳方案为P(T(1),j),P(T(2),j),...,P(T(n),j),即此时的最少钱币个数∑==n1j)P(T(k),),(k j n C ,则P(T(2),j),...,P(T(n),j)一定是利用T[n]中n 个不同的面值钱币对钱数j=j-P(T(1),j)* T(1)进行兑换零钱的最佳方案。

其次,找零钱问题具有重叠于问题性质:a)当n=1时,即只能用一种钱币兑换零钱,钱币的面值为T[0],有b)当n>1时,若j>T[n],即第n 种钱币面值比所兑换零钱数小,因此有}1])[,({),(min 1+-=≤≤k T j n C j n C n k 。

当k 为n)i (1k 0≤≤时,C(n,j)达到最小值,有P(T(k0),j)=P(T(k ),j-T(k ))+1若j=T[n],即用n 种钱币兑换零钱,第n 种钱币面值与兑换零钱数j 相等,此时有C(n,j)=C(n,T[n])=1;{][,1][,0])[,(),(n T i n T i n T i P j i P =≠==若j<T[n],即第n 种钱币面值比所兑换零钱数大,因此兑换零钱只需考虑前n-1种钱币即可,故有C(n,j)=C(n-1,j),且P(T(n-1),j)=0。

从以上讨论可知该问题具有重叠子问题性质。

(2) 根据分析建立正确的递归关系。

答:0]1[%0]1[%≠=T j T j {∞=]1[/),1(T j j C 0]1[%0]1[%≠=T j T j {∞=]1[/),1(T j j C{T[i];j 0 j)1,-C(i T[i]j 1)T[i])-j C(i,j),1,-min(C(i j)C(i,<≤≥+=(3) 分析利用你的想法解决该问题可能会有怎样的时空复杂度。

答:算法的时间复杂度主要取决于程序的两个循环,所以算法的时间复杂度为)O(n 2;算法执行过程中引入了一个二维数组,随着输入规模的增大,所需要的空间复杂度为:)O(n 2五、问题解决(1) 根据对问题的分析,写出解决办法。

答:设数组T[]中存放的是n 种钱币递增的不同面值,所要找的钱数为M,M 由用户输入;数组C[j]表示利用数T[n]兑换零钱数为j 时所用的最少钱币个数,即最优值;P[i][j](1<=i<=n)表示按照上述最优值兑换零钱J 时用到钱币面值为第i 种钱币的个数。

(2) 描述你在进行实现时,主要的函数或操作内部的主要算法;分析这个算法的时、空复杂度,并说明你设计的巧妙之处,如有创新,将其清晰的表述。

for (i=0;i<kind;i++) { for (j=i+1;j<kind;j++) if (T[i]>T[j]) { Swap(T[i],T[j]); } }long temptotal=total; if (total>0) for (i=kind-1;i>=0;i--) { Swap(T[i],T[kind-1]); if (T[kind-1]>0) { c[kind-1]=temptotal/T[kind-1]; long tempcount=0; while((c[kind-1]>0)&&(c[kind-1]<=mincount)) {tempcount=c[kind-1];temptotal=temptotal-T[kind-1]*c[kind-1]; for (j=kind-2;j>=0;j--) if ((temptotal>0)&&(T[j]>0)) { c[j]=temptotal/T[j]; temptotal=temptotal-T[j]*c[j]; tempcount=tempcount+c[j]; }if ((tempcount>0)&&(tempcount<mincount)&&(temptotal==0)) mincount=tempcount; c[kind-1]=c[kind-1]-1; temptotal=total; tempcount=0; } }}时间复杂度:从上面算法可知,最优值c[』]的计算过程中,最外层为循环for(j=1;j<=M;j++)嵌套着while(k>1&&flag==0)循环,而while(k>1&flag==0)循环中又嵌套着三个并列的for 循环。

因此本算法最坏情况下的复杂度是O(M*2n );最好的情况当然是里面for 循环的条件不满足而不执行,此时的复杂度为O(M*n)。

其中:M 表示需要兑换的零钱数,对于M 来说,该值一般不是很大,对于钱币来说,M 会小于100元,即10 000分;n 表示钱币的种类,n 值一般不会很大.如钱币总的有13种(从1分,2分,⋯,100元)。

经过以上分析,如是最坏情况时的复杂度应为O(M*2n ),则该值对于内存和运行速度较小的自动售货机等的应用前景则不会很好。

但本算法中的递归结构在M>T[n]时,有1])[,(j),C(n min k 1+-=≤≤k T j n C n。

可见对于钱币j=M 时,求c(n,j)时,并不要求对从1≤i ≤j ,的所有情况都要求c(n,i)+1,而是只求1])[,(+-k T j n C 。

其中:1≤k ≤n 。

钱币一般只有13种左右,因此其效率大为上升。

最坏的情况下需要执行)O(n )n O(M 32=⨯,而M 小于100元即10000分,远大于n 。

本算法的动态规划算法的时间复杂性比该问题的一般动态规划算法的效率要好得多。

该算法的时间复杂性是310数量级的.对于应用于自动售货机等运行速度较慢的机器来说是不成问题的。

空间复杂度:从上面算法可知,用到了三个数组,分别为T[n],c[j],P[i][j]。

其中:i<=n,j<=M 。

空间复杂性主要由P[1][j]决定,为O(M ×n)。

P(i,j)中的i 指的T[n]中的值.对于钱币来说一般n 为13左右。

该算法的空间复杂度为O(M x n)=O(f),而M 小于100元即10 000分,远大于n 。

该算法动态规划的空间复杂性比该问题的一般动态规划的效率要好得10数量级,这对于应用到小内存的自动售货多。

该算法的空间复杂性为2机来说是没有任何问题的。

(3)你在调试过程中发现了怎样的问题?又做了怎样的改进?答:在调试过程中,我发现对于该算法最主要的在于矩阵C[i,j]的求解,而算法的递归关系没有弄明白,所以在求解C[i,j]时总是出现问题,后来在查询了资料后,将C[i,j]递归关系的实现改为c[j]=temptotal/T[j];temptotal=temptotal-T[j]*c[j];tempcount=tempcount+c[j];解决了该问题。

(4)写出用你的测试数据按照算法的流程填写的算法中的存储结构。

C[1,2,3]={0,2,9}。

六、实验结果总结1.程序运行截图:2.回答以下问题:(1)算法实现的复杂度在问题规模很大时可以接受吗?答:可以接受,因为动态规划算法有很好的效率,所以当问题复杂度很大时,就不会影响到算法的运行时间。

(2)如果不用动态规划方法还能想到其他的解决方式吗?和动态规划相比会有更好的效率吗?答:对于找硬币问题,有时候贪心算法也能解决,但不如动态规划求解有效率,所以采用动态规划方法是一个很好的选择。

(3)所选用的数据结构合适吗?答:采用了数组的数据结构,合适,因为该数据结构能够支持对于数组中的元素的随机访问,而且方便查询。

(4)该算法都存在哪几类可能出现的情况,你的测试完全覆盖了你所想到的这些情况吗,测试结果如何?(5)叙述通过实验你对动态规划方法的理解及其优缺点答:优点:动态规划方法有效利用了子问题的重叠性,减少了大量的计算。

而且动态规划的使用也非常简单,只需要问题满足最优子结构、子问题重叠性、无后效性即可。

通常可以利用分置思想构造出子问题的分解方法,就可以利用动态规划。

缺点:动态规划的缺点并不是最快的方法,只是解决某一类型的问题的工具或者优化某些 NPC问题的时间效率。

动态规划的很重要的一点就是用大量空间换取时间上的优化,所以这并不是一个完美的方法。

七、附录参考资料:《算法导论》。

相关文档
最新文档