算法设计与分析期末复习题(一)
✅作者简介:大家好我是编程ID
个人主页:编程ID的csdn博客
系列专栏:算法
推荐一款编程题刷题神器点击跳转进入网站
1、二分搜索算法是利用( A )实现的算法。
A、分治策略 B、动态规划法 C、贪心法 D、回溯法
2、下列不是动态规划算法基本步骤的是( A )。
A、找出最优解的性质 B、构造最优解 C、算出最优解 D、定义最优解
3、最大效益优先是( A )的一搜索方式。
A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
4、最长公共子序列算法利用的算法是( B )。
A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
5. 回溯法解TSP问题时的解空间树是( A )。
A、子集树 B、排列树 C、深度优先生成树 D、广度优先生成树
6.下列算法中通常以自底向上的方式求解最优解的是( B )。
A、备忘录法 B、动态规划法 C、贪心法 D、回溯法
7、衡量一个算法好坏的标准是(C )。
A 运行速度快 B 占用空间少 C 时间复杂度低 D 代码短
8、以下不可以使用分治法求解的是(D )。
A 棋盘覆盖问题 B 选择问题 C 归并排序 D 0/1背包问题
9. 实现循环赛日程表利用的算法是( A )。
A、分治策略 B、动态规划法 C、贪心法 D、回溯法
10、实现最长公共子序列利用的算法是( B )。
A、分治策略 B、动态规划法 C、贪心法 D、回溯法
11.下面不是分支界限法搜索方式的是( D )。
A、广度优先 B、最小耗费优先 C、最大效益优先 D、深度优先
12.下列算法中通常以深度优先方式系统搜索问题解的是( D )。
A、备忘录法 B、动态规划法 C、贪心法 D、回溯法
13. 一个问题可用动态规划算法或贪心算法求解的关键特征是问题的( B )。
A、重叠子问题 B、最优子结构性质 C、贪心选择性质 D、定义最优解
14.广度优先是( A )的一搜索方式。
A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
15.背包问题的贪心算法所需的计算时间为( B )。
A、O(n2n) B、O(nlogn) C、O(2n) D、O(n)
16.实现最大子段和利用的算法是( B )。
A、分治策略 B、动态规划法 C、贪心法 D、回溯法
17.实现棋盘覆盖算法利用的算法是( A )。
A、分治法 B、动态规划法 C、贪心法 D、回溯法
18.下面是贪心算法的基本要素的是( C )。
A、重叠子问题 B、构造最优解 C、贪心选择性质 D、定义最优解
19.回溯法的效率不依赖于下列哪些因素( D )
A.满足显约束的值的个数 B. 计算约束函数的时间
C. 计算限界函数的时间 D. 确定解空间的时间
20.下面哪种函数是回溯法中为避免无效搜索采取的策略( B )
A.递归函数 B.剪枝函数 C。随机数函数 D.搜索函数
21、以深度优先方式系统搜索问题解的算法称为 ( D ) 。
A、分支界限算法 B、概率算法 C、贪心算法 D、回溯算法
22、贪心算法与动态规划算法的主要区别是( B )。
A、最优子结构 B、贪心选择性质 C、构造最优解 D、定义最优解
23. 采用最大效益优先搜索方式的算法是( A )。
A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
24. ( D )是贪心算法与动态规划算法的共同点。
A、重叠子问题 B、构造最优解 C、贪心选择性质 D、最优子结构性质
25. 矩阵连乘问题的算法可由( B)设计实现。
A、分支界限算法 B、动态规划算法 C、贪心算法 D、回溯算法
26. 0-1背包问题的回溯算法所需的计算时间为( A )
A、O(n2n) B、O(nlogn) C、O(2n) D、O(n)
27、背包问题的贪心算法所需的计算时间为( B )
A、O(n2n) B、O(nlogn) C、O(2n) D、O(n)
29、使用分治法求解不需要满足的条件是(A )。
A 子问题必须是一样的 B 子问题不能够重复C 子问题的解可以合并 D 原问题和子问题使用相同的方法解
30、下面问题(B )不能使用贪心法解决。
A 单源最短路径问题 B N皇后问题 C 最小花费生成树问题 D 背包问题
31、下列算法中不能解决0/1背包问题的是(A )
A 贪心法 B 动态规划 C 回溯法 D 分支限界法
32、回溯法搜索状态空间树是按照(C )的顺序。
A 中序遍历 B 广度优先遍历 C 深度优先遍历 D 层次优先遍历
33、采用广度优先策略搜索的算法是( A )。
A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
34.实现合并排序利用的算法是( A )。
A、分治策略 B、动态规划法 C、贪心法 D、回溯法
35.下列是动态规划算法基本要素的是( D )。
A、定义最优解 B、构造最优解 C、算出最优解 D、子问题重叠性质
36.下列算法中通常以自底向下的方式求解最优解的是( B )。
A、分治法 B、动态规划法 C、贪心法 D、回溯法
二、 填空题
1.算法的复杂性有 时间 复杂性和 空间 复杂性之分。
2、程序是 算法 用某种程序设计语言的具体实现。
3、算法的“确定性”指的是组成算法的每条 指令 是清晰的,无歧义的。
4.矩阵连乘问题的算法可由 动态规划 设计实现。
5、算法是指解决问题的 一种方法 或 一个过程 。
6、快速排序算法的性能取决于 划分的对称性 。
7、从分治法的一般设计模式可以看出,用它设计出的程序一般是 递归算法 。
8、问题的 最优子结构性质 是该问题可用动态规划算法或贪心算法求解的关键特征。
9、以深度优先方式系统搜索问题解的算法称为 回溯法 。
10、任何可用计算机求解的问题所需的时间都与其 规模 有关。
11、计算一个算法时间复杂度通常可以计算 循环次数 、 基本操作的频率 或计算步。
12、回溯法搜索解空间树时,常用的两种剪枝函数为 约束函数 和 限界函数
14、解决0/1背包问题可以使用动态规划、回溯法和分支限界法,其中不需要排序的是 动态规划 ,需要排序的是 回溯法 ,分支限界法 。
15、使用回溯法进行状态空间树裁剪分支时一般有两个标准:约束条件和目标函数的界,N皇后问题和0/1背包问题正好是两种不同的类型,其中同时使用约束条件和目标函数的界进行裁剪的是 0/1背包问题 ,只使用约束条件进行裁剪的是 N皇后问题 。
17、回溯法是一种既带有 系统性 又带有 跳跃性 的搜索算法。
18. 动态规划算法的两个基本要素是. 最优子结构性质和 重叠子问题 性质
19.贪心算法的基本要素是 贪心选择 质和 最优子结构 性质 。
21. 动态规划算法的基本思想是将待求解问题分解成若干 子问题 ,先求解 子问题 ,然后从这些 子问题 的解得到原问题的解。
算法是由若干条指令组成的有穷序列,且要满足输入,输出 、确定性和 有限性 四条性质。
23、快速排序算法是基于 分治策略 的一种排序算法。
24、以广度优先或以最小耗费方式搜索问题解的算法称为 分支限界法 。
三、算法设计题
1.背包问题的贪心算法, 分支限界算法
2.最大子段和: 动态规划算法
3.贪心算法求活动安排问题
5.快速排序
6. 多机调度问题-贪心算法
四、简答题
1分治法的基本思想
2. 分治法与动态规划法的相同点
3. 分治法与动态规划法的不同点
4. 分支限界法与回溯法的相同点
5.分治法所能解决的问题一般具有的几个特征是:
6. 用分支限界法设计算法的步骤
7. 回溯法中常见的两类典型的解空间树是子集
8. 请简述符号t(n)∈θ(g(n)), t(n)∈Ω(g(n)),t(n)∈Ο(g(n))的含义。
9. 分支限界法的搜索策略是:
算法设计与分析期末复习题(二)
蒈一、选择题(20分)
薆1.最长公共子序列算法利用的算法是(???B???)。
莆A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
膄2.实现棋盘覆盖算法利用的算法是(???A???)。
蒁A、分治法 B、动态规划法 C、贪心法 D、回溯法
薆3.下面是贪心算法的基本要素的是(???C???)。
薃A、重叠子问题 B、构造最优解 C、贪心选择性质 D、定义最优解
蚂4.回溯法的效率不依赖于下列哪些因素(D)
芀A.满足显约束的值的个数 B.计算约束函数的时间
蚆C.计算限界函数的时间 D.确定解空间的时间
羄5.下面哪种函数是回溯法中为避免无效搜索采取的策略(???B???)
莄A.递归函数 B.剪枝函数 C。随机数函数 D.搜索函数
罿6.采用最大效益优先搜索方式的算法是(???A???)。
聿A、分支界限法 B、动态规划法 C、贪心法 D、回溯法
莅7.贪心算法与动态规划算法的主要区别是(??B???)。
螂A、最优子结构 B、贪心选择性质 C、构造最优解 D、定义最优解
肂8.实现最大子段和利用的算法是(???B???)。
腿A、分治策略 B、动态规划法 C、贪心法 D、回溯法
螆9.优先队列式分支限界法选取扩展结点的原则是(???C???)。
薄A、先进先出 B、后进先出 C、结点的优先级 D、随机
螁10.下列算法中通常以广度优先方式系统搜索问题解的是(???A??)。
艿A、分支限界法 B、动态规划法 C、贪心法 D、回溯法
膇二、填空题(22分每空2分)
羂1.算法是由若干条指令组成的有穷序列,且要满足输入、输出、确定性和有限性四条性质。
薀2、大整数乘积算法是用分治法来设计的。
艿3、以广度优先或以最小耗费方式搜索问题解的算法称为分支限界法。
芄4、舍伍德算法总能求得问题的一个解。
蚄5、贪心选择性质是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
荿6.快速排序
荿template
蚅voidQuickSort(Typea[],intp,intr)
膁{
莁if(p 葿intq=Partition(a,p,r); 肅QuickSort(a,p,q-1);//对左半段排序 袃QuickSort(a,q+1,r);//对右半段排序 膀} 蕿} 蒆7.哈密顿环问题的算法可由回溯法设计实现。 芁8.贪心算法不一定产生最优解。 衿9.算法中通常以深度优先方式系统搜索问题解的是回溯法。 虿三、算法设计与分析(25分) 蚃1.用欧几里德迭代算法求两个数的最小公倍数(10分) 莃#include 羈usingnamespacestd; 聿intGcd(intm,intn) 莄{ 螁 if(m==0) 肁 returnn; 腿 if(n==0) 螅 returnm; 薃 intt=m>n?n:m; 螀 while(m%t||n%t) 艿 t–; 膆 returnt; 羁} 蕿intmain() 芈{ 薇 inta,b; 蚃 cout<<“Inputa&b(0<=a
薂 cin>>a>>b; 莈 intm=a*b/Gcd(a,b); 蚄 cout<<“TheLeastCommonMultipleis:”< 蒅 return0; 莁2、试用动态规划算法实现最大子矩阵和问题:求矩阵A的一个子矩阵,使其各元素之各为最大。(15分) 蒈解:解答如下: 肅intMaxSum2(intm,intn,int**a) 袃{ 膀intsum=0; 薈int*b=newint[n+1]; 蒆for(inti=1;i<=m;i++){ 薄for(intk=1;k<=n;k++)b[k]=0;………………………………(5分) 罿for(intj=i;j<=m;j++){ 羂for(intk=1;k<=n;k++)b[k]+=a[j][k]; 膁intmax=MaxSum(n,b); 芆if(max>sum)sum=max; 芅} 羂} 芇returnsum;………………………………(10分) 肈} 羄intMaxSum(intn,int*a) 肂{ 蚈intsum=0,b=0; 蒆for(inti=1;i<=n;i++){ 螃if(b>0)b+=a[i]; 膂elseb=a[i]; 聿if(b>sum)sum=b; 膈} 螆Returnsum;………………………………(15分) 芁} 薀四、简答题(33分) 蚆1、假设有7个物品,它们的重量和价值如下表所示。若这些物品均可以被分割,且背包容量M=150,如果使用贪心方法求解此背包问题,使收益最大,请写出求解过程请写出求解过程。(10分) 薅物品 肃重量 袄价值 蚆解:使用单位价值从大到小:FBGDECA,得到贪心解为:FBGD全部放入,而E放入87.5%,得到价值为190.625。 袁2.写出分治算法MaxMin算法对下列实例中找最大数和最小数的过程。(10分) 螈数组A=(48,12,61,3,5,19,32,7) 袇解:1、48,12,61,3,5,19,32,7表中元素多于二,对半分割 蒅2、48,1261,35,1932,7表中元素多于二,对半分割 羀3、48~61,12~319~32,5~7 腿求前半部分的最大最小和后半部分的最大最小,两个半部分的大者为最大,小者为最小 蕿4、61~323~5两个半部分的大者为最大,小者为最小 芄5、613寻找结束 肀3.写出多段图最短路经动态规划算法求解下列实例的过程,并求出最优值。(13分) 薀 膀484 腿3 羆545 各边的代价如下: 根据这个规律,可以归纳出下面的递 template #include 这是来源于国际象棋的一个问题。皇后可以沿着纵横和两条斜线 4 个方向相互捕捉。 1 2 3 4 5 6 7 8 求解过程从空配置开始。在第 1 列至第 m 列为合理配置的基础上,再配置第 m+1 列, 在编写程序之前,先确定边式棋盘的数据结构。比较直观的方法是采用一个二维数组, 初始时,所有行和斜线上均没有皇后,从第 1 列的第 1 行配置第一个皇后开始,在第 int n,m,good; 试探法找解算法也常常被编写成递归函数,下面两程序中的函数 queen_all()和函数 10 int n; 采用递归方法找一个解与找全部解稍有不同,在找一个解的算法中,递归算法要对当 int n; 先进先出(F I F O) 即从活节点表中取出节点的顺序与加入节点的顺序相同,因此活 最小耗费或最大收益法在这种模式中,每个节点都有一个对应的耗费或收益。如果查找 其中,g(n)表示 。 算法 MATCHAIN 一.填空题: 二.计算题和简答题: 将n=, a=1, c=2, g(n)=, d=1代入该类递归方程解的一般形式得: 三.算法填空题: 四.算法设计题: 最坏情况下的时间复杂性:Θ(n) A.3,15,130,20,98,67 B.3,15,20,130,98,67 图中空白处理框①和②处应填入的是( ) A.将①处改为 i ← 0 B.将②处改为 s ≥ 0 ? 二、填空题 三、简答题 2.请简述蛮力算法的优点? 3.算法设计与分析过程的典型步骤都包括哪些? 4.请简述分治法的基本思路? 5.请简述减治法的基本思路? 6.请简述递归算法设计的基本思路? 7.请简述变治法的基本思路? 四、算法实现题 2.斐波那契数列:0,1,1,2,3,5,8,13,21,34,…… 请编写Java应用程序,由键盘输入n的值代表要生成斐波那契数列的项数,在屏幕上输出n项斐波那契数列。 3.编写基于Java语言的选择排序算法。 4.编写基于Java语言的冒泡排序算法。 5.编写基于Java语言的顺序查找算法。 功能:该算法实现顺序查找功能 输入:一个整数数组a[ ]和一个要查找的键值k 输出:如果在数组中找到k,则返回对应数组元素的下标;如果在数组中找不到k,则返回-1 } 6.编写基于Java语言的折半查找算法。 7.编写基于Java语言的字符串匹配算法。 功能:该算法实现字符串匹配功能 输入:一个n个字符的字符串str代表一段文本 输出:如果查找成功的话,返回文本的第一个匹配字符串中第一个字符的位置, } 8.编写基于Java语言的直接插入排序算法。 经常采用的算法主要有迭代法、分而治之法、贪婪法、动态规划法、回溯法、分支限界法 迭代法 一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只? 分而治之法 贪婪法 【问题】 背包问题 #include }
莁A
羁B
莈C
莄D
蒁E
肈F
袆G
薁35
葿30
薈60
膆50
薁40
袀10
羅25
蚁10
芀40
蚇30
蚃50
螁35
蚁40
膅30
2
216
C(1,2)=3,C(1,3)=5,C(1,4)=2
C(2,6)=8,C(2,7)=4,C(3,5)=5,C(3,6)=4,C(4,5)=2,C(4,6)=1
C(5,8)=4,C(6,8)=5,C(7,8)=6
解:Cost(4,8)=0
Cost(3,7)=C(7,8)+0=6,
Cost(3,6)=C(6,8)+0=5,
Cost(3,5)=C(5,8)+0=4
Cost(2,4)=min{C(4,6)+Cost(3,6),C(4,5)+Cost(3,5)}
=min{1+5,2+4}=6
Cost(2,3)=min{C(3,6)+Cost(3,6)}
=min{4+5}=9
Cost(2,2)=min{C(2,6)+Cost(3,6),C(2,7)+Cost(3,7)}
=min{8+5,4+6}=10
Cost(1,1)=min{C(1,2)+Cost(2,2),C(1,3)+Cost(2,3),C(1,4)+Cost(2,4)}
=min{3+10,5+9,2+6}=8
按上所述,Cost(1,1)为所求最优值。
算法设计与分析期末复习题(三)
1
《算法设计与分析》历年期末试题整理(含答案) (1)用计算机求解问题的步骤:
1、问题分析 2、数学模型建立 3、算法设计与选择 4、算法指标 5、算法分析 6、算法实现 7、
程序调试 8、结果整理文档编制
(2)算法定义:算法是指在解决问题时,按照某种机械步骤一定可以得到问题结果的处理
过程
(3)算法的三要素
1、操作 2、控制结构 3、数据结构
算法具有以下 5 个属性:
有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成。
确定性:算法中每一条指令必须有确切的含义。不存在二义性。只有一个入口和一个出
口
可行性:一个算法是可行的就是算法描述的操作是可以通过已经实现的基本运算执行有
限次来实现的。
输入:一个算法有零个或多个输入,这些输入取自于某个特定对象的集合。
输出:一个算法有一个或多个输出,这些输出同输入有着某些特定关系的量。
算法设计的质量指标:
正确性:算法应满足具体问题的需求;
可读性:算法应该好读,以有利于读者对程序的理解;
健壮性:算法应具有容错处理,当输入为非法数据时,算法应对其作出反应,而不是产
生莫名其妙的输出结果。
效率与存储量需求:效率指的是算法执行的时间;存储量需求指算法执行过程中所需要
的最大存储空间。一般这两者与问题的规模有关。
经常采用的算法主要有迭代法、分而治之法、贪婪法、动态规划法、回溯法、分支限界法
迭代法 也称“辗转法”,是一种不断用变量的旧值递推出新值的解决问题的方
法。
利用迭代算法解决问题,需要做好以下三个方面的工作:
一、确定迭代模型。在可以用迭代算法解决的问题中,至少存在一个直接或
间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
二、建立迭代关系式。所谓迭代关系式,指如何从变量的前一个值推出其下
一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以
使用递推或倒推的方法来完成。
三、对迭代过程进行控制。在什么时候结束迭代过程?这是编写迭代程序必
须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可
分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是
所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实
现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的
条件。
编写计算斐波那契(Fibonacci)数列的第 n 项函数 fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
2
fib(0)=0;
fib(1)=1;
fib(n)=fib(n-1)+fib(n-2) (当 n>1 时)。
写成递归函数有:
int fib(int n)
{ if (n0) return 0;
if (n1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,
每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到
第 12 个月时,该饲养场共有兔子多少只?
分析: 这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为
u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……
根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 =
4 ,……
推公式:
u n = u n - 1 × 2 (n ≥ 2)
对应 u n 和 u n - 1 ,定义两
个迭代变量 y 和 x ,可将上面的递
推公式转换成如下迭代关系:
y=x2
x=y
让计算机对这个迭代关系重复执
行 11 次,就可以算出第 12 个月时
的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x2
x=y
next i
print y
end
分而治之法
1、分治法的基本思想
任何一个可以用计算机求解的问题所需的计算时间都与其规模 N 有关。问题
的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于 n 个元素的
排序问题,当 n=1 时,不需任何计算;n=2 时,只要作一次比较即可排好序;n=3 时
只要作 3 次比较即可,…。而当 n 较大时,问题就不那么容易处理了。要想直接解决
一个规模较大的问题,有时是相当困难的。
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的
相同问题,以便各个击破,分而治之。
分治法所能解决的问题一般具有以下几个特征:
(1)该问题的规模缩小到一定的程度就可以容易地解决;
3
(2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结
构性质;
(3)利用该问题分解出的子问题的解可以合并为该问题的解;
(4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共
的子子问题。
3、分治法的基本步骤
分治法在每一层递归上都有三个步骤:
(1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同
的子问题;
(2)解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子
问题;
(3)合并:将各个子问题的解合并为原问题的解。
快速排序
在这种方法中, n 个元素被分成三段(组):左段 l e f t,右段 r i g h t 和中段 m i d d l e。中
段仅包含一个元素。左段中各元素都小于等于中段元素,右段中各元素都大于等于中段元素。
因此 l e f t 和 r i g h t 中的元素可以独立排序,并且不必对 l e f t 和 r i g h t 的排序结果进行合
并。m i d d l e 中的元素被称为支点( p i v o t )。图 1 4 - 9 中给出了快速排序的伪代码。
/ /使用快速排序方法对 a[ 0 :n- 1 ]排序
从 a[ 0 :n- 1 ]中选择一个元素作为 m i d d l e,该元素为支点
把余下的元素分割为两段 left 和 r i g h t,使得 l e f t 中的元素都小于等于支点,而 right
中的元素都大于等于支点
递归地使用快速排序方法对 left 进行排序
递归地使用快速排序方法对 right 进行排序
所得结果为 l e f t + m i d d l e + r i g h t
考察元素序列[ 4 , 8 , 3 , 7 , 1 , 5 , 6 , 2 ]。假设选择元素 6 作为支点,则 6 位于 m i d d l e; 4,3,1,5,2 位于 l e f t;8,7 位于 r i g h t。当 left 排好序后,所得结果为 1,2,3,4, 5;当 r i g h t 排好序后,所得结果为 7,8。把 right 中的元素放在支点元素之后, l e f t 中
的元素放在支点元素之前,即可得到最终的结果[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]。
把元素序列划分为 l e f t、m i d d l e 和 r i g h t 可以就地进行(见程序 1 4 - 6)。在程序
1 4 - 6 中,支点总是取位置 1 中的元素。也可以采用其他选择方式来提高排序性能,本章稍
后部分将给出这样一种选择。
程序 14-6 快速排序
void QuickSort(T*a, int n)
{// 对 a[0:n-1] 进行快速排序
{// 要求 a[n] 必需有最大关键值
quickSort(a, 0, n-1);
template
void quickSort(T a[], int l, int r)
{// 排序 a [ l : r ], a[r+1] 有大值
if (l >= r) return;
int i = l, // 从左至右的游标
j = r + 1; // 从右到左的游标
T pivot = a[l];
// 把左侧>= pivot 的元素与右侧<=
pivot 的元素进行交换
while (true) {
do {// 在左侧寻找>= pivot 的元素
i = i + 1;
} while (a < pivot);
do {// 在右侧寻找<= pivot 的元素
j = j - 1;
} while (a[j] > pivot);
if (i >= j) break; // 未发现交换对象
4
Swap(a, a[j]);
}
// 设置 p i v o t
a[l] = a[j];
a[j] = pivot;
quickSort(a, l, j-1); // 对左段排序
quickSort(a, j+1, r); // 对右段排序
}
贪婪法
它采用逐步构造最优解的思想,在问题求解的每一个阶段,都作出一个在一 定标准下看上
去最优的决策;决策一旦作出,就不可再更改。制定决策的依据称为贪婪准则。
贪婪法是一种不追求最优解,只希望得到较为满意解的方法。贪婪法一般可以快速得到
满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪婪法常以当
前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪婪法不要回溯。
【问题】 背包问题
问题描述:有不同价值、不同重量的物品 n 件,求从这 n 件物品中选取一部分物品的选择方案,使
选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
void main()
{
int
m,n,i,j,w[50],p[50],pl[50],b[50],s=0,max;
printf(“输入背包容量 m,物品种类 n :”);
scanf(“%d %d”,&m,&n);
for(i=1;i<=n;i=i+1)
{
printf(“输入物品的重量 W 和价值
P :”);
scanf(“%d %d”,&w[i],&p[i]);
pl[i]=p[i];
s=s+w[i];
}
if(s<=m)
{
printf(“whole choose\n”);
//return;
}
for(i=1;i<=n;i=i+1)
{
max=1;
for(j=2;j<=n;j=j+1)
if(pl[j]/w[j]>pl[max]/w[max])
max=j;
pl[max]=0;
b[i]=max;
}
for(i=1,s=0;s
if(s!=m)
w[b[i-1]]=m-w[b[i-1]];
for(j=1;j<=i-1;j=j+1)
printf(“choose weight %d\n”,w[b[j]]);
}动态规划的基本思想
前文主要介绍了动态规划的一些理论依据,我们将前文所说的具有明显的阶段划分和状态转
移方程的动态规划称为标准动态规划,这种标准动态规划是在研究多阶段决策问题时推导出
来的,具有严格的数学形式,适合用于理论上的分析。在实际应用中,许多问题的阶段划分
并不明显,这时如果刻意地划分阶段法反而麻烦。一般来说,只要该问题可以划分成规模更
小的子问题,并且原问题的最优解中包含了子问题的最优解(即满足最优子化原理),则可
以考虑用动态规划解决。
5
动态规划的实质是分治思想和解决冗余,因此,动态规划是一种将问题实例分解为更小的、
相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。
由此可知,动态规划法与分治法和贪心法类似,它们都是将问题实例归纳为更小的、相似的
子问题,并通过求解子问题产生一个全局最优解。
贪心法的当前选择可能要依赖已经作出的所有选择,但不依赖于有待于做出的选择和子问
题。因此贪心法自顶向下,一步一步地作出贪心选择;
而分治法中的各个子问题是独立的(即不包含公共的子问题),因此一旦递归地求出各子问
题的解后,便可自下而上地将子问题的解合并成问题的解。
不足之处:如果当前选择可能要依赖子问题的解时,则难以通过局部的贪心策略达到全局最
优解;如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题。
解决上述问题的办法是利用动态规划。该方法主要应用于最优化问题,这类问题会有多种可
能的解,每个解都有一个值,而动态规划找出其中最优(最大或最小)值的解。若存在若干
个取最优值的解的话,它只取其中的一个。在求解过程中,该方法也是通过求解局部子问题
的解达到全局最优解,但与分治法和贪心法不同的是,动态规划允许这些子问题不独立,(亦
即各子问题可包含公共的子问题)也允许其通过自身子问题的解作出选择,该方法对每一个
子问题只解一次,并将结果保存起来,避免每次碰到时都要重复计算。
因此,动态规划法所针对的问题有一个显著的特征,即它所对应的子问题树中的子问题呈现
大量的重复。动态规划法的关键就在于,对于重复出现的子问题,只在第一次遇到时加以求
解,并把答案保存起来,让以后再遇到时直接引用,不必重新求解。
3、动态规划算法的基本步骤
设计一个标准的动态规划算法,通常可按以下几个步骤进行:
(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段
一定要是有序的或者是可排序的(即无后向性),否则问题就无法用动态规划求解。
(2)选择状态:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。
当然,状态的选择要满足无后效性。
(3)确定决策并写出状态转移方程:之所以把这两步放在一起,是因为决策和状态转移有
着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以,如果
我们确定了决策,状态转移方程也就写出来了。但事实上,我们常常是反过来做,根据相邻
两段的各状态之间的关系来确定决策。
(4)写出规划方程(包括边界条件):动态规划的基本方程是规划方程的通用形式化表达式。
一般说来,只要阶段、状态、决策和状态转移确定了,这一步还是比较简单的。动态规划的
主要难点在于理论上的设计,一旦设计完成,实现部分就会非常简单。根据动态规划的基本
方程可以直接递归计算最优值,但是一般将其改为递推计算,实现的大体上的框架如下:
标准动态规划的基本框架
for k:=n downto 1 do
for 每一个xk∈Xk do
for 每一个uk∈Uk(xk) do
begin
fk(xk):=一个极值; {∞或-∞} xk+1:=Tk(xk,uk); {状态转移方程}
t:=φ(fk+1(xk+1),vk(xk,uk)); {基本方程(9)式}
if t比fk(xk)更优 then fk(xk):=t; {计算fk(xk)的最优值}
end;
6
t:=一个极值; {∞或-∞}
for 每一个x1∈X1 do
if f1(x1)比t更优 then t:=f1(x1); {按照 10 式求出最优指标}
输出 t;
但是,实际应用当中经常不显式地按照上面步骤设计动态规划,而是按以下几个步骤进行:
(1)分析最优解的性质,并刻划其结构特征。
(2)递归地定义最优值。
(3)以自底向上的方式或自顶向下的记忆化方法(备忘录法)计算出最优值。
(4)根据计算最优值时得到的信息,构造一个最优解。
步骤(1)~(3)是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤(4)可
以省略,若需要求出问题的一个最优解,则必须执行步骤(4)。此时,在步骤(3)中计算
最优值时,通常需记录更多的信息,以便在步骤(4)中,根据所记录的信息,快速地构造
出一个最优解。
总结:动态规划实际上就是最优化的问题,是指将原问题的大实例等价于同一最优化问题的
较小实例,自底向上的求解最小实例,并将所求解存放起来,存放的结果就是为了准备数据。
与递归相比,递归是不断的调用子程序求解,是自顶向下的调用和求解。
回溯法
回溯法也称为试探法,该方法首先暂时放弃关于问题规模大小的限制,并将问题的候选
解按某种顺序逐一枚举和检验。当发现当前候选解不可能是解时,就选择下一个候选解;倘
若当前候选解除了还不满足问题规模要求外,满足所有其他要求时,继续扩大当前候选解的
规模,并继续试探。如果当前候选解满足包括问题规模在内的所有要求时,该候选解就是问
题的一个解。在回溯法中,放弃当前候选解,寻找下一个候选解的过程称为回溯。扩大当前
候选解的规模,以继续试探的过程称为向前试探。
1、回溯法的一般描述
可用回溯法求解的问题P,通常要能表达为:对于已知的由n元组(x1,x2,…,xn)组成的
一个状态空间E={(x1,x2,…,xn)∣xi∈Si ,i=1,2,…,n},给定关于n元组中的一个
分量的一个约束集D,要求E中满足D的全部约束条件的所有n元组。其中Si是分量xi的定义
域,且 |Si| 有限,i=1,2,…,n。我们称E中满足D的全部约束条件的任一n元组为问题P
的一个解。
解问题 P 的最朴素的方法就是枚举法,即对 E 中的所有 n 元组逐一地检测其是否满足 D 的
全部约束,若满足,则为问题 P 的一个解。但显然,其计算量是相当大的。
我们发现,对于许多问题,所给定的约束集D具有完备性,即i元组(x1,x2,…,xi)满足
D中仅涉及到x1,x2,…,xi的所有约束意味着j(j D中仅涉及到x1,x2,…,xj的所有约束,i=1,2,…,n。换句话说,只要存在 0≤j≤n-1,
使得(x1,x2,…,xj)违反D中仅涉及到x1,x2,…,xj的约束之一,则以(x1,x2,…,
xj)为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)一定也违反D中仅涉及到x1,x2,…,
xi的一个约束,n≥i>j。因此,对于约束集D具有完备性的问题P,一旦检测断定某个j元组(x1, x2,…,xj)违反D中仅涉及x1,x2,…,xj的一个约束,就可以肯定,以(x1,x2,…,xj)
为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)都不会是问题P的解,因而就不必去
搜索它们、检测它们。回溯法正是针对这类问题,利用这类问题的上述性质而提出来的比枚
举法效率更高的算法。
回溯法首先将问题 P 的 n 元组的状态空间 E 表示成一棵高为 n 的带权有序树 T,把在 E 中
求问题 P 的所有解转化为在 T 中搜索问题 P 的所有解。树 T 类似于检索树,它可以这样构
7
造:
设Si中的元素可排成xi
(1) ,xi
(2) ,…,xi
(mi-1) ,|Si| =mi,i=1,2,…,n。从根开始,
让T的第I层的每一个结点都有mi个儿子。这mi个儿子到它们的双亲的边,按从左到右的次
序,分别带权xi+1
(1) ,xi+1
(2) ,…,xi+1
(mi) ,i=0,1,2,…,n-1。照这种构造方式,E中的
一个n元组(x1,x2,…,xn)对应于T中的一个叶子结点,T的根到这个叶子结点的路径上
依次的n条边的权分别为x1,x2,…,xn,反之亦然。另外,对于任意的 0≤i≤n-1,E中n
元组(x1,x2,…,xn)的一个前缀I元组(x1,x2,…,xi)对应于T中的一个非叶子结点,
T的根到这个非叶子结点的路径上依次的I条边的权分别为x1,x2,…,xi,反之亦然。特别,
E中的任意一个n元组的空前缀(),对应于T的根。
因而,在E中寻找问题P的一个解等价于在T中搜索一个叶子结点,要求从T的根到该叶
子结点的路径上依次的n条边相应带的n个权x1,x2,…,xn满足约束集D的全部约束。在T
中搜索所要求的叶子结点,很自然的一种方式是从根出发,按深度优先的策略逐步深入,即
依次搜索满足约束条件的前缀 1 元组(x1i)、前缀 2 元组(x1,x2)、…,前缀I元组(x1, x2,…,xi),…,直到i=n为止。
在回溯法中,上述引入的树被称为问题 P 的状态空间树;树 T 上任意一个结点被称为问
题 P 的状态结点;树 T 上的任意一个叶子结点被称为问题 P 的一个解状态结点;树 T 上满
足约束集 D 的全部约束的任意一个叶子结点被称为问题 P 的一个回答状态结点,它对应于
问题 P 的一个解。
【问题】 n 皇后问题
问题描述:求出在一个 n×n 的棋盘上,放置 n 个不能互相捕捉的国际象棋“皇后”
的所有布局。
如图所示,一个皇后放在棋盘的第 4 行第 3 列位置上,则棋盘上凡打“×”的位置上的皇后
就能与这个皇后相互捕捉。
× ×
× × ×
× × ×
× × Q × × × × ×
× × ×
× × ×
× ×
× ×
从图中可以得到以下启示:一个合适的解应是在每列、每行上只有一个皇后,且一条
斜线上也只有一个皇后。
直至第 n 列配置也是合理时,就找到了一个解。接着改变第 n 列配置,希望获得下一个解。
另外,在任一列上,可能有 n 种配置。开始时配置在第 1 行,以后改变时,顺次选择第 2
行、第 3 行、…、直到第 n 行。当第 n 行配置也找不到一个合理的配置时,就要回溯,去改
变前一列的配置。得到求解皇后问题的算法如下:
8
{ 输入棋盘大小值 n;
m=0;
good=1;
do {
if (good)
if (m==n)
{ 输出解;
改变之,形成下一个候选解;
}
else 扩展当前候选接至下一列;
else 改变之,形成下一个候选解;
good=检查当前候选解的合理性;
} while (m!=0);
}
但仔细观察就会发现,这种表示方法给调整候选解及检查其合理性带来困难。更好的方法乃
是尽可能直接表示那些常用的信息。对于本题来说,“常用信息”并不是皇后的具体位置,
而是“一个皇后是否已经在某行和某条斜线合理地安置好了”。因在某一列上恰好放一个皇
后,引入一个一维数组(col[
]),值 col[i]表示在棋盘第 i 列、col[i]行有一个皇后。例如:col[3]=4,就表示在棋盘
的第 3 列、第 4 行上有一个皇后。另外,为了使程序在找完了全部解后回溯到最初位置,设
定 col[0]的初值为 0 当回溯到第 0 列时,说明程序已求得全部解,结束程序运行。
为使程序在检查皇后配置的合理性方面简易方便,引入以下三个工作数组:
(1) 数组 a[ ],a[k]表示第 k 行上还没有皇后;
(2) 数组 b[ ],b[k]表示第 k 列右高左低斜线上没有皇后;
(3) 数组 c[ ],c[k]表示第 k 列左高右低斜线上没有皇后;
棋盘中同一右高左低斜线上的方格,他们的行号与列号之和相同;同一左高右低斜线
上的方格,他们的行号与列号之差均相同。
m 列 col[m]行放置了一个合理的皇后后,准备考察第 m+1 列时,在数组 a[
]、b[ ]和 c[ ]中为第 m 列,col[m]行的位置设定有皇后标志;当从第 m 列回溯到第
m-1 列,并准备调整第 m-1 列的皇后配置时,清除在数组 a[
]、b[ ]和 c[ ]中设置的关于第 m-1 列,col[m-1]行有皇后的标志。一个皇后在 m 列,
col[m]行方格内配置是合理的,由数组 a[ ]、b[ ]和 c[ ]对应位置的值都为 1 来确定。细节见
以下程序:
【程序】include
include
define MAXN 20
9
int col[MAXN+1],a[MAXN+1],b[2MAXN+1],c[2MAXN+1];
void main()
{ int j;
char awn;
printf(“Enter n: “); scanf(“%d”,&n);
for (j=0;j<=n;j++) a[j]=1;
for (j=0;j<=2*n;j++) cb[j]=c[j]=1;
m=1; col[1]=1; good=1; col[0]=0;
do {
if (good)
if (mn)
{ printf(“列\t 行”);
for (j=1;j<=n;j++)
printf(“%3d\t%d\n”,j,col[j]);
printf(“Enter a character (Q/q for exit)!\n”);
scanf(“%c”,&awn);
if (awn’Q’||awn==’q’) exit(0);
while (col[m]==n)
{ m–;
a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=1;
}
col[m]++;
}
else
{ a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=0;
col[++m]=1;
}
else
{ while (col[m]==n)
{ m–;
a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=1;
}
col[m]++;
}
good=a[col[m]]&&b[m+col[m]]&&c[n+m-col[m]];
} while (m!=0);
}
queen_one()能分别用来解皇后问题的全部解和一个解。
【程序】include
include
define MAXN 20
int col[MAXN+1],a[MAXN+1],b[2MAXN+1],c[2MAXN+1];
void main()
{ int j;
printf(“Enter n: “); scanf(“%d”,&n);
for (j=0;j<=n;j++) a[j]=1;
for (j=0;j<=2*n;j++) cb[j]=c[j]=1;
queen_all(1,n);
}
void queen_all(int k,int n)
{ int i,j;
char awn;
for (i=1;i<=n;i++)
if (a[i]&&b[k+i]&&c[n+k-i])
{ col[k]=i;
a[i]=b[k+i]=c[n+k-i]=0;
if (kn)
{ printf(“列\t 行”);
for (j=1;j<=n;j++)
printf(“%3d\t%d\n”,j,col[j]);
printf(“Enter a character (Q/q for exit)!\n”);
scanf(“%c”,&awn);
if (awn’Q’||awn==’q’) exit(0);
}
queen_all(k+1,n);
a[i]=b[k+i]=c[n+k-i];
}
}
前候选解最终是否能成为解要有回答。当它成为最终解时,递归函数就不再递归试探,立即
返回;若不能成为解,就得继续试探。设函数 queen_one()返回 1 表示找到解,返回 0 表示
当前候选解不能成为解。细节见以下函数。
【程序】define MAXN 20
int col[MAXN+1],a[MAXN+1],b[2MAXN+1],c[2MAXN+1];
int queen_one(int k,int n)
{ int i,found;
i=found=0;
While (!found&&i { i++;
11
if (a[i]&&b[k+i]&&c[n+k-i])
{ col[k]=i;
a[i]=b[k+i]=c[n+k-i]=0;
if (k==n) return 1;
else
found=queen_one(k+1,n);
a[i]=b[k+i]=c[n+k-i]=1;
}
}
return found;
}
分支定界法:
分支限界法:
这是一种用于求解组合优化问题的排除非解的搜索算法。类似于回溯法,分枝定界法在搜索
解空间时,也经常使用树形结构来组织解空间。然而与回溯法不同的是,回溯算法使用深度
优先方法搜索树结构,而分枝定界一般用宽度优先或最小耗费方法来搜索这些树。因此,可
以很容易比较回溯法与分枝定界法的异同。相对而言,分枝定界算法的解空间比回溯法大得
多,因此当内存容量有限时,回溯法成功的可能性更大。
算法思想:分枝定界(branch and bound)是另一种系统地搜索解空间的方法,它与回溯法
的主要区别在于对 E-节点的扩充方式。每个活节点有且仅有一次机会变成 E-节点。当一个
节点变为 E-节点时,则生成从该节点移动一步即可到达的所有新节点。在生成的节点中,
抛弃那些不可能导出(最优)可行解的节点,其余节点加入活节点表,然后从表中选择一个
节点作为下一个 E-节点。从活节点表中取出所选择的节点并进行扩充,直到找到解或活动
表为空,扩充过程才结束。
有两种常用的方法可用来选择下一个 E-节点(虽然也可能存在其他的方法):
节点表的性质与队列相同。
一个具有最小耗费的解,则活节点表可用最小堆来建立,下一个 E-节点就是具有最小耗费
的活节点;如果希望搜索一个具有最大收益的解,则可用最大堆来构造活节点表,下一个
E-节点是具有最大收益的活节点
装载问题
用一个队列 Q 来存放活结点表,Q 中 weight
表示每个活结点所相应的当前载重量。当
weight=-1 时,表示队列已达到解空间树
同一层结点的尾部。
算法首先检测当前扩展结点的左儿子结
点是否为可行结点。如果是则将其加入到活
结点队列中。然后将其右儿子结点加入到活
结点队列中(右儿子结点一定是可行结点)。2
个儿子结点都产生后,当前扩展结点被舍
弃。
活结点队列中的队首元素被取出作为
当前扩展结点,由于队列中每一层结点之后
都有一个尾部标记-1,故在取队首元素时,
活结点队列一定不空。当取出的元素是-1
时,再判断当前队列是否为空。如果队列非
空,则将尾部标记-1 加入活结点队列,算法
开始处理下一层的活结点。
/该版本只算出最优解/
#include
#include
struct Queue{
int weight ;
12
struct Queue* next ;
};
int bestw = 0 ; // 目前的最优值
Queue* Q; // 活结点队列
Queue* lq = NULL ;
Queue* fq = NULL ;
int Add(int w)
{
Queue* q ;
q = (Queue*)malloc(sizeof(Queue)) ;
if(q NULL)
{
printf(“没有足够的空间分配\n”) ;
return 1 ;
}q->next = NULL ;
q->weight = w ;
if(Q->next == NULL)
{ Q->next = q ;
fq = lq = Q->next ; //一定要使元素放到链
中}
else
{
lq->next = q ;
lq = q ;
// lq = q->next ;
}
return 0 ;
}
int IsEmpty()
{
if(Q->nextNULL)
return 1 ;
return 0 ;
}
int Delete(int&w)
{
Queue* tmp = NULL ;
// fq = Q->next ;
tmp = fq ;
w = fq->weight ;
Q->next = fq->next ; /一定不能丢了链表头
/
fq = fq->next ;
free(tmp) ;
return 0 ;
}
void EnQueue(int wt,
int& bestw, int i, int n) //该函数负责加入
活结点
{ // 如果不是叶结点,则将结点权值 wt 加
入队列 Q
if (i == n)
{ // 叶子
if (wt>bestw)
bestw = wt;
}
else
Add(wt); // 不是叶子
}
int MaxLoading(int w[], int c, int n)
{ // 返回最优装载值
// 为层次 1 初始化
int err ; //返回值
int i = 1; // 当前扩展结点的层
int Ew = 0; // 当前扩展结点的权值
bestw = 0; // 目前的最优值
Q = (Queue)malloc(sizeof(Queue)) ;
Q->next = NULL ;
Q->weight = -1 ;
err = Add(-1) ; //标记本层的尾部
if(err)
{
return 0 ;
}
while (true)
{
// 检查左孩子结点
if (Ew + w[i] <= c) // x[i] = 1
EnQueue(Ew + w[i], bestw , i, n);
// 右孩子总是可行的
EnQueue(Ew, bestw, i, n); // x[i] = 0
Delete(Ew); // 取下一个扩展结点
if (Ew == -1)
{ // 到达层的尾部
13
if (IsEmpty())
return bestw;
if(i
Delete(Ew); // 取下一扩展结点
i++; // 进入下一层
}
}}
int main()
{
int n =0 ;
int c = 0 ;
int i = 0 ;
int
FILE in , out ;
in = fopen(“input.txt” , “r”) ;
out = fopen(“output.txt” , “w”) ;
if(inNULL||outNULL){
printf(“没有输入输出文件\n”) ;
return 1 ;
}
fscanf(in , “%d” , &n) ;
fscanf(in , “%d” , &c) ;
w = (int)malloc(sizeof(int)(n+1)) ;
for(i =1 ; i<=n ; i++)
fscanf(in , “%d” , &w[i]) ;
MaxLoading(w , c , n) ;
fprintf(out , “%d\n” , bestw) ;
return 0 ;
}
算法设计与分析期末复习题(四)答案在下面
一.填空题(每空2分,共30分)
1.算法的时间复杂性指算法中 的执行次数。
2.在忽略常数因子的情况下,O、和三个符号中, 提供了算法运行时间的一个上界。
3.设Dn表示大小为n的输入集合,t(I)表示输入为I时算法的运算时间, p(I)表示输入I出现的概率,则算法的平均情况下时间复杂性A(n)= 。
4.分治算法的时间复杂性常常满足如下形式的递归方程:
6.回溯算法的基本思想是 。
7.动态规划和分治法在分解子问题方面的不同点是 。
8.贪心算法中每次做出的贪心选择都是 最优选择。
9.PQ式的分支限界法中,对于活结点表中的结点,其下界函数值越小,优先级越 。
10.选择排序、插入排序和归并排序算法中, 算法是分治算法。
11.随机算法的一个基本特征是对于同一组输入, 不同的运行可能得到 的结果。
12.对于下面的确定性快速排序算法,只要在步骤3前加入随机化步骤 ,就可得到一个随机化快速排序算法,该随机化步骤的功能是 。
算法 QUICKSORT
输入:n个元素的数组A[1…n]。
输出:按非降序排列的数组A中的元素。
end QUICKSORT
过程 quicksort(A, low, high)
// 对A[low…high]中的元素按非降序排序。
//算法SPLIT以A[low]为主元将A[low…high]划分成两部 //分,返回主元的新位置。
6. end if
end quicksort
13.下面算法的基本运算是 运算,该算法的时间复杂性阶为( )。
算法 SPLIT
输入:正整数n,数组A[1…n]。
输出:…。
i=1
x=A[1]
for j=2 to n
if A[j]<=x then
i=i+1
if ij then A[i]A[j]
end if
end for
A[i]A[1]
w =i
return w, A
end SPLIT
二.计算题和简答题(每小题7分,共21分)
1.用O、、表示函数f与g之间阶的关系,并分别指出下列函数中阶最低和最高的函数:
(1) f (n)=100 g(n)=
(2) f(n)=6n+n g(n)=3n
(3) f(n)= n/logn-1 g(n)=
(4) f(n)= g(n)=
(5) f(n)= g(n)=
2.下面是一个递归算法,其中,过程pro1和pro2的运算时间分别是1和。给出该算法的时间复杂性T(n)满足的递归方程,并求解该递归方程,估计T(n)的阶(用表示)。
算法 EX1
输入:正整数n,n=2k。
输出:…
ex1(n)
end EX1
过程 ex1(n)
if n=1 then
pro1(n)
else
pro2(n)
ex1(n/2)
end if
return
end ex1
3.用Floyd算法求下图每一对顶点之间的最短路径长度,计算矩阵D0,D1,D2和D3,其中Dk[i, j]表示从顶点i到顶点j的不经过编号大于k的顶点的最短路径长度。
三.算法填空题(共34分)
1.(10分)设n个不同的整数按升序存于数组A[1…n]中,求使得A[i]=i的下标i 。下面是求解该问题的分治算法。
算法 SEARCH
输入:正整数n,存储n个按升序排列的不同整数的数组A[1…n]。
输出:A[1…n]中使得A[i]=i的一个下标i,若不存在,则输出 no solution。
i=find ( (1) )
if i>0 then output i
else output “no solution”
end SEARCH
过程 find (low, high)
// 求A[low…high] 中使得A[i]=i的一个下标并返回,若不存在,//则返回0。
if (2) then return 0
else
mid=
if (3) then return mid
else
if A[mid]
else
return (5)
end if
end if
end if
end find
2.(10分) 下面是求解矩阵链乘问题的动态规划算法。
矩阵链乘问题:给出n个矩阵M1, M2, …, Mn , Mi 为riri+1阶矩阵,i=1, 2, …, n,求计算M1M2…Mn所需的最少数量乘法次数。
记 Mi, j=MiMi+1…Mj , i<=j。设C[i, j], 1<=i<=j<=n, 表示计算Mi, j的所需的最少数量乘法次数,则
输入:矩阵链长度n, n个矩阵的阶r[1…n+1], 其中r[1…n]为n个矩阵的行数,r[n+1]为第n个矩阵的列数。
输出:n个矩阵链乘所需的数量乘法的最少次数。
for i=1 to n C[i, i]= (1)
for d=1 to n-1
for i=1 to n-d
j= (2)
C[i, j]= ∞
for k=i+1 to j
x= (3)
if x
end if
end for
end for
end for
return (5)
end MATCHAIN
3.(14分) 下面是用回溯法求解马的周游问题的算法。
马的周游问题:给出一个nxn棋盘,已知一个中国象棋马在棋盘上的某个起点位置(x0, y0),求一条访问每个棋盘格点恰好一次,最后回到起点的周游路线。(设马走日字。)
算法 HORSETRAVEL
输入:正整数n,马的起点位置(x0, y0),1<=x0, y0<=n 。
输出:一条从起点始访问nxn棋盘每个格点恰好一次,最后回到起点的周游路线;若问题无解,则输出no solution。
tag[1…n, 1…n]=0
dx[1…8]={2, 1, -1, -2, -2, -1, 1, 2}
dy[1…8]={1, 2, 2, 1, -1, -2, -2, -1}
flag=false
x=x0; y=y0 ; tag[x, y]=1
m=n*n
i=1; k[i]=0
while (1) and not flag
while k[i]<8 and not flag
k[i]= (2)
x1= x+dx[k[i]]; y1= y+dy[k[i]]
if ((x1,y1)无越界and tag[x1, y1]=0) or ((x1,y1)=(x0,y0) and i=m) then
x=x1; y=y1
tag[x, y]= (3)
if i=m then flag=true
else
i= (4)
(5)
end if
end if
end while
i=i-1
(6)
(7)
end while
if flag then outputroute(k) //输出路径
else output “no solution”
end HORSETRAVEL
四.算法设计题(15分)
10. 一个旅行者要驾车从A地到B地,A、B两地间距离为s。A、B两地之间有n个加油站,已知第i个加油站离起点A的距离为公里,0=,车加满油后可行驶m公里,出发之前汽车油箱为空。应如何加油使得从A地到B地沿途加油次数最少?给出用贪心法求解该最优化问题的贪心选择策略,写出求该最优化问题的最优值和最优解的贪心算法,并分析算法的时间复杂性。
《算法设计与分析》期考试卷(A)标准答案
随机选主元
n
(1) f(n)= O(g(n))
(2) f(n)=(g(n))
(3) f(n)=(g(n))
(4) f(n)= O(g(n))
(5) f(n)=(g(n))
阶最低的函数是:100
阶最高的函数是:
T(n)=1+=1+k-
=1+ k-=++1
所以,T(n)= ++1=。
3.
1.(1) 1, n (2) low>high (3) A[mid]=mid
(4) mid+1, high (5) find(low, mid-1)
2. (1) 0 (2) i+d (3) C[i, k-1]+C[k, j]+r[i]*r[k]*r[j+1]
(4) C[i, j] (5) C[1, n]
3. (1) i>=1 (2)k[i]+1 (3) 1
(4) i+1 (5) k[i]=0 (6) tag[x, y]=0
(7) x=x-dx[k[i]]; y=y-dy[k[i]]
算法 MINSTOPS
输入:A、B两地间的距离s,A、B两地间的加油站数n,车加满油后可行驶的公里数m,存储各加油站离起点A的距离的数组d[1…n]。
输出:从A地到B地的最少加油次数k以及最优解x[1…k](x[i]表示第i次加油的加油站序号),若问题无解,则输出no solution。
d[n+1]=s; //设置虚拟加油站第n+1站。
for i=1 to n
if d[i+1]-d[i]>m then
output “no solution”; return //无解,返回
end if
end for
k=1; x[k]=1 //在第1站加满油。
s1=m //s1为用汽车的当前油量可行驶至的地点与A点的距离
i=2
while s1 if d[i+1]>s1 then //以汽车的当前油量无法到达第i+1站。
k=k+1; x[k]=i //在第i站加满油。
s1=d[i]+m //刷新s1的值
end if
i=i+1
end while
output k, x[1…k]
MINSTOPS
算法设计与分析期末复习题(五)
一、选择题
1.一个.java文件中可以有( )个public类。
A.一个 B.两个 C.多个 D.零个
2.一个算法应该是( )
A.程序 B.问题求解步骤的描述
C.要满足五个基本特性 D.A和C
3.用计算机无法解决“打印所有素数”的问题,其原因是解决该问题的算法违背了算法特征中的( )
A.唯一性 B.有穷性 C.有0个或多个输入 D.有输出
4.某校有6位学生参加学生会主席竞选,得票数依次为130,20,98,15,67,3。若采用冒泡排序算法对其进行排序,则完成第二遍时的结果是( )
C.3,15,20,67,130,98 D.3,15,20,67,98,130
5.下列关于算法的描述,正确的是( )
A.一个算法的执行步骤可以是无限的 B.一个完整的算法必须有输出
C.算法只能用流程图表示 D.一个完整的算法至少有一个输入
6.Java Application源程序的主类是指包含有( )方法的类。
A、main方法 B、toString方法 C、init方法 D、actionPerfromed方法
7.找出满足各位数字之和等于5的所有三位数可采用的算法思路是( )
A.分治法 B.减治法 C.蛮力法 D.变治法
8.在编写Java Application程序时,若需要使用到标准输入输出语句,必须在程序的开头写上( )语句。
A、import java.awt.* ; B、import java.applet.Applet ;
C、import java.io.* ; D、import java.awt.Graphics ;
9.计算某球队平均年龄的部分算法流程图如图所示,其中:c用来记录已输入球员的人数,sum用来计算有效数据之和,d用来存储从键盘输入的球员年龄值,输入0时表示输入结束。
A.① sum ← sum + d B.① sum ← sum + c
② c ← c + 1 ② c ← c + 1
C.① sum ← sum + d D.① sum ← sum + c
② d ← d + 1 ② d ← d + 1
10.报名参加冬季越野赛跑的某班5位学生的学号是:5,8,11,33,45。利用折半查找,查找学号为33号学生的过程中,依次被访问到的学号是( )
A.5,11,33 B.8,33 C.11,45,33 D.11,33
11.表达式(short)8/9.2*5的值的类型为
A.short B. int C.double D.float
12. 设x为int型变量,则执行一下语句段后,x的值为
x=10;
x+=x-=x-x;
A.10 B.20 C.40 D.30
13.下列代码的执行结果是
public class StringTest{
public static void main(String args[]){
int a=4,b=6,c=8;
String s=”abc”;
System.out.println(a+b+s+c);
System.out.printin(); }
}
A.ababcc B.464688 C.46abc8 D.10abc8
14. 下列程序段执行后t3的结果是
int t1 = 2, t2 = 3, t3;
t3=t1
15.要计算当0〈x〈10时,y=x,应当使用的语句是
A.if(0
第一趟:2,12,16,88,5,10
第二趟:2,5,16,88,12,10
第三趟:2,5,10,88,12,16
则采用的排序方法是( )
A.冒泡排序 B.合并排序 C.快速排序 D.选择排序
17.类与对象的关系是( )
A. 建筑图纸和建筑物的关系 B. 汽车与发动机的关系
C. 人与黑人的关系 D. 没有关系
18.JAVA语言二维数组定义中,第二维的长度 ( )
A.可以不相等 B.必须相等
C.高维数组长度与低维数组长度相同 D.固定长度
19.算法必须具备( )这三个特性。
A.可执行性、可移植性、可扩充性 B.可执行性、确定性、有穷性
C.确定性、有穷性、稳定性 D.易读性、稳定性、安全性
20.如下图所示,该流程图所表示的算法违背了算法的有穷性特征,下列修改方法中,可以改正该错误的是( )
C.将③处改为 i ← i-2 D.将④处改为 s ← s-i
1.一个显而易见的事实是:大部分算法的执行时间随着 输入量的增加 而增大。
2.算法是 求解某一问题所使用的一系列清晰的指令 。
3.算法分析时间效率模型的基本数学公式是: T(n) ≈ CopC(n) 。
4.算法设计技术是 用算法解题的一般性方法 ,用于解决不同计算领域的多种问题。
5.三个渐进符号: O 、 Ω 和 Ө 。
6.效率分析框架主要关心一个算法的 基本操作次数的增长次数 ,并把它作为算法效率的主要指标。
7.Java源程序的文件名和程序中定义的 主类名 应保持一致,包括字母大小写的匹配。
8.算法中常见的问题类型包括: 排序 、 查找 、字符串处理和组合问题等。
9.类中的 构造 方法是一个特殊的方法,其名称与类名相同。
10.面向对象程序设计语言中的3个重要特性分别是 封装 、 继承 和 多态 。
11.Java源程序文件的扩展名为 java ,编译生成的字节码文件的扩展名为 class 。
12.大多数算法的效率可以分为常数、 对数 、线性、平方、 立方 和指数等。
1.什么是算法?算法的五个重要特征是什么?
答:算法是求解某一问题所使用的一系列清晰的指令。
答:
(1)输入:有零个或多个由外部提供的量作为算法的输入.
(2)输出:算法产生至少一个量作为输出.
(3)确定性:组成算法的每条指令是清晰的,无歧义的.
(4)有限性:在执行了有穷步骤后运算终止.
(5)可行性:运算都是基本运算,原理上能在有限时间内完成.
答:
蛮力算法是一种简单直接地解决问题的方法。蛮力法具有如下优点:(1)应用范围广;(2)不受实例规模的限制;(3)当要解决的问题实例不多,设计更高效算法的代价太大时可选用;(4)对解决一些小规模的问题实例仍然有效;(5)可作为衡量其他算法的参照物。
答:
(1)了解问题的内容
(2)了解计算设备的性能
(3)在精确解法和近似解法之间选择
(4)确定适当的数据结构
(5)算法设计技术
(6)详细表述算法的方法
(7)证明算法的正确性
(8)分析算法
(9)为算法写代码
答:
将规模为N的问题分解为k个规模较小的子问题,使这些子问题相互独立可分别求解,再将k个子问题的解合并成原问题的解。如子问题的规模仍很大,则反复分解直到问题小到可直接求解为止。
在分治法中,子问题的解法通常与原问题相同,自然导致递归过程。
答:
减治技术利用了一个问题给定实例的解和同样问题较小实例的解之间的某种关系。一旦建立了这种关系,既可以从顶至底(递归地),也可以从底至顶(非递归地)来运用该关系。
减治法有三种主要的变种:
减常数(如1)::每此迭代规模减小n→n-1
减因子(如1/2):每此迭代规模减半n→ n/2
减可变规模:每此迭代减小的规模不同
答:
递归的执行过程由分解过程和求值过程两部分构成。
实际上, 递归思路是把一个不能或不好直接求解的“大问题”转化成一个或几个“小问题”来解决,再把这些“小问题”进一步分解成更小的“小问题”来解决,如此分解,直至每个“小问题”都可以直接解决(此时分解到递归出口)。
但递归分解不是随意的分解,递归分解要保证“大问题”与“小问题”相似,即求解过程与环境都相似。并且有一个分解的终点。从而使问题可解。
答:
变治法的技术基于变换思想。变治法分为两个阶段的工作:首先在“变”的阶段,出于这样或那样的原因,将问题的实例变得更容易求解;然后是“治”的阶段,对问题的实例进行求解。
根据对问题实例的变换方式不同,变治法有三种主要的类型:
(1)实例化简——变换为同样问题的一个更简单或者更方便的实例;
(2)改变表现——变换为同样实力的不同表现;
(3)问题化简——变换为另一个问题的实例,这种问题的算法是已知的。
8.请简述时空权衡法的基本思路?
答:
时空权衡法的基本思路是对问题的部分或全部输入做预处理,然后对得到的额外信息使用额外的存储空间来存储。通过实现更快或更方便的数据存取,以加速后面问题的求解来提高算法的效率。
1.对于任意非负整数n,计算阶乘函数F(n) = n!的值。因为当n ≥ 1时,n!= 1×2×3×……×(n-1)×n = (n-1)!×n。并且根据定义,0!= 1,所以可以使用下面的递归算法计算n!:F(n) = F(n-1) × n。
请编写Java应用程序,由键盘输入n的值,在屏幕上输出计算的n!的结果。
import java.io.*;
public class FN
{
static long f(int n)
{
long r = 1;
if(n != 0)
r = n * f(n-1);
return r;
}
public static void main(String args[]) throws IOException
{
//输入N的值
byte[] buf = new byte[10];
System.out.println(“请输入一个整数:”);
System.in.read(buf);
String str=new String(buf);
int n=Integer.parseInt(str.trim());
//计算N!的值
long result = f(n);
//输出结果
System.out.println(n + “!=” + result);
}
}
这个数列可以用一个简单的递推式和两个初始条件来定义:
当n > 1时,F(n) = F(n-1) + F(n-2)
F(0) = 0,F(1) = 1
import java.io.*;
public class Fb{
/斐波那契数列算法/
int f(int n){
int r;
if(n <= 1)
r = n;
else
r = f(n-1) + f(n-2);
return r;
}
public static void main(String args[]) throws IOException{
System.out.println(“请输入所求斐波那契数列的项数:”);
byte buf[] = new byte[20];
System.in.read(buf);
String t1 = new String(buf);
int n = Integer.parseInt(t1.trim());
Fb f1 = new Fb();
int b;
System.out.println(“输出包含” + n + “项的斐波那契数列:”);
for(int i = 0; i <= n; i++)
{
b = f1.f(i);
System.out.print(b + " ");
}
System.out.println();
}
}
/***
***/
public void selectionSort (int a[ ])
{
int temp,min;
for(int i=0;i
min = i;
for(int j=i+1;j
min = j;
temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
/***
***/
public void bubbleSort(int a[ ])
{
int temp;
for(int i=0;i
{
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
/***
***/
public int seqSearch(int a[ ],int k)
{
int i = 0;
while((i < a.length ) && ( a[i] != k ))
i = i + 1;
if( i < a.length) return i;
else
return -1;
/***
***/
public int binarySearch(int a[ ], int k)
{
int low = 0;
int upper = a.length - 1;
while(low <= upper)
{
int mid = (low+upper) / 2;
if(k == a[mid])
return mid;
else
if(des < a[mid])
upper = mid - 1;
else
low = mid + 1;
}
return -1;
}
/***
一个m个字符的字符串key代表一个模式
否则返回-1
***/
public int stringMatch(String str,String key)
{
int j;
int n = str.length();
int m = key.length(); for(int i = 0; i <= (n - m); i++)
{
j = 0;
while((j < m) && (key.charAt(j) == str.charAt(i+j)))
{
j = j + 1;
System.out.println(i + "," + j);
if(j == m)
return i;
}
}
return -1;
/***
***/
public void insertSort (int a[ ])
{
int temp,i,j;
for(i=1;i
temp=a[i];
j=i-1;
while(j>=0 && a[j]>=temp)
{
a[j+1]=a[j];
j–;
}
a[j+1]=temp;
}
}
算法设计与分析期末复习题(六)
1、用计算机求解问题的步骤:
1、问题分析2、数学模型建立3、算法设计与选择4、算法指标5、算法分析6、算法实现7、程序调试8、结果整理文档编制
2、算法定义:算法是指在解决问题时,按照某种机械步骤一定可以得到问题结果的处理过程
3、算法的三要素
1、操作2、控制结构3、数据结构
算法具有以下5个属性:
有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成。
确定性:算法中每一条指令必须有确切的含义。不存在二义性。只有一个入口和一个出口
可行性:一个算法是可行的就是算法描述的操作是可以通过已经实现的基本运算执行有限次来实现的。
输入:一个算法有零个或多个输入,这些输入取自于某个特定对象的集合。
输出:一个算法有一个或多个输出,这些输出同输入有着某些特定关系的量。
算法设计的质量指标:
正确性:算法应满足具体问题的需求;
可读性:算法应该好读,以有利于读者对程序的理解;
健壮性:算法应具有容错处理,当输入为非法数据时,算法应对其作出反应,而不是产生莫名其妙的输出结果。
效率与存储量需求:效率指的是算法执行的时间;存储量需求指算法执行过程中所需要的最大存储空间。一般这两者与问题的规模有关。
基本思想:迭代法也称“辗转法”,是一种不断用变量的旧值递推出新值的解决问题的方法。
解题步骤:1、确定迭代模型。根据问题描述,分析得出前一个(或几个)值与其下一个值的迭代关系数学模型。
2、建立迭代关系式。迭代关系式就是一个直接或间接地不断由旧值递推出新值的表达式,存储新值的变量称为迭代变量
3、对迭代过程进行控制。确定在什么时候结束迭代过程,这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
写成递归函数有:
int fib(int n)
{ if (n0) return 0;
if (n1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
Main()
{int I,a=1,b=1;
Print(a,b);
For(i=1;i<=10;i++)
{
C=a+b;
Print©;
A=b;
B=c;
}
}
1、基本思想
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治法所能解决的问题一般具有以下几个特征:
(1)该问题的规模缩小到一定的程度就可以容易地解决;
(2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
(3)利用该问题分解出的子问题的解可以合并为该问题的解;
(4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
3、分治法的基本步骤
(1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
(2)解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
(3)合并:将各个子问题的解合并为原问题的解。
基本思想:以逐步的局部最优,达到最终的全局最优。无后效性
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
void main()
{
int m,n,i,j,w[50],p[50],pl[50],b[50],s=0,max;
printf(“输入背包容量m,物品种类n :”);
scanf(“%d %d”,&m,&n);
for(i=1;i<=n;i=i+1)
{
printf(“输入物品的重量W和价值P :”);
scanf(“%d %d”,&w[i],&p[i]);
pl[i]=p[i];
s=s+w[i];
}
if(s<=m)
{
printf(“whole choose\n”);
//return;
}
for(i=1;i<=n;i=i+1)
{
max=1;
for(j=2;j<=n;j=j+1)
if(pl[j]/w[j]>pl[max]/w[max])
max=j;
pl[max]=0;
b[i]=max;
}
for(i=1,s=0;s
if(s!=m)
w[b[i-1]]=m-w[b[i-1]];
for(j=1;j<=i-1;j=j+1)
printf(“choose weight %d\n”,w[b[j]]);
动态规划
基本思想:把求解的问题分成许多阶段或多个子问题,然后按顺序求解各个子问题。前一个子问题的解为后一个子问题的求解提供了有用的信息。在求解任何一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解,依次解决各子问题,最后一个子问题就是问题的解。
基本步骤
(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段一定要是有序的或者是可排序的(即无后向性),否则问题就无法用动态规划求解。
(2)选择状态:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
(3)确定决策并写出状态转移方程:之所以把这两步放在一起,是因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以,如果我们确定了决策,状态转移方程也就写出来了。但事实上,我们常常是反过来做,根据相邻两段的各状态之间的关系来确定决策。
回溯法
基本思想:按照深度优先策略,从根结点出发搜索解空间。算法搜索至解空间的任一结点时总是先判断该结点是否问题的约束条件。如果满足进入该子树,继续按深度优先的策略搜索。否则,不去搜索以该结点为根的子树,而是逐层向其祖先结点回溯。其实回溯法就是对隐式图的深度优先搜索算法
基本步骤:
1、确定问题的解空间:应用回溯法时,首先应明确定义问题的解的空间。问题的解空间应至少包含问题的一个解。
2、确定结点的扩展规则
3、搜索解空间:从开始结点出发,以深度优先的方式搜索整个解空间。
【问题】 n皇后问题
分支限界法
基本思想: 分支限界法是由“分支”和“限界”两部分组成。“分支”策略体现在对问题空间是按广度优先的策略进行搜索“限界”策略是为了加速搜索速度面采用启发信息剪枝策略。
可能会让画出“子集树斩图”235页例题及图好好看一下。
整理不易喜欢的点个赞呗!