《算法设计与问题求解》公开课学习笔记2

第二章 枚举算法(大道至简)

枚举算法的基本原理

1. 思考

“水仙花数”也称为n = 3 的Narcissus数,定义为 n 等于3的Narcissus数,如:153 = 1^3 + 5^3 + 3^3, 请找出所有的水仙花数。

解决:列举所有的3位数(100~999),判断各位数字的立方和是否等于该数本身。

2. 什么是枚举算法 

枚举算法也称之为穷举算法,就是按照问题本身的性质,一一列举出所有可能的解,并在列举的过程中,逐一检验每个可能的解是否是问题的真正解。若是则采纳这个解,否则抛弃它。

在枚举的过程中注意两个基本原则:(1)不能遗漏,可能导致结果不正确;(2)不能重复,可能导致算法运算效率较低。

枚举算法的特点:枚举算法的解具有准确和全面的特点;其次枚举算法实现简单,一般通过循环/递归实现;执行效率较低,算法有较大的优化空间。

枚举算法的设计步骤:

1)确定枚举对象。枚举对象也可以理解为是问题解的表达形式,一般需要用若干参数(p1, p2, ..., pk)来描述。参数之间需要相互独立,而且参数数目越小问题解的搜索空间的维度也相应地小,每个参数的取值范围越小,问题解的搜索空间也越小。

2)逐一列举所有可能解。根据枚举对象的参数构造循环,一一列举其表达式的每一种取值情况。

3)逐一验证可能解。根据问题解的要求,一一验证枚举对象表达式的每一个取值,如果满足条件则采纳它,否则抛弃。

3. 模糊数字问题的枚举算法

任务描述:一张单据上有一个5位数的编码,因为保管不善,其百威数已经变的模糊不清,但是知道这个5位数是57和67的倍数。现在要设计一个算法,输出所有满足这些条件的5位数,并统计这样的数的个数。

输入:每一行对应一个测试样例,每一行包含4个数字,依次是万位数,千位数,十位数和个位数。最后一行包含4个-1,表示输入结束。

输出:每组测试样例的结果输出占一行,第一个数字表示满足条件的编码个数,后面按升序输出所有满足条件的编码,数字与数字之间用空格隔开。

分析过程:首先获得枚举对象Obj(h):记h百位数数字,h = 0 ~ 9;逐一列举 for (h=0; h<10; h++);逐一验证 19h95 是否能够同时整数 57 和 67。

4. 百钱百鸡问题的枚举算法设计

任务描述:我国古代数学家张丘建在《算经》中出了一道题,“鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?”现在假定各种鸡的价格不变,拥有的钱数为m,需要购买的鸡数为n,试求出所有可能的购买方案总数。

输入:每行对应一个测试样例,每一行包含2个数字,分别为 n 和 m。最后一行包含2个-1,表示输入结束。

输出:每组测试样例的结果输出占一行,输出可能购买的方案总数。

分析过程:首先确定枚举对象 obj(n1, n2, n3) 分别代表鸡翁、鸡母。鸡雏的数量,其取值均为0~n;接下来逐一列举(for 循环)逐一验证,验证条件是 n1 + n2 + n3 == n && 5*n1 + 3*n2 + n3/3 == m。

算法优化:上述直接枚举的算法复杂度为O(n^3),由于枚举对象n1, n2, n3不是相互独立的,因此其中一个参数可以用总和 n 和 ;另外两根参数表示,则此时枚举对象为两个参数 obj(n1, n2),鸡翁数为 n1,鸡母数为 n2,鸡雏数为 n-n1-n2。则可将算法中的

三层 for 循环优化为两层 for 循环,提高算法的效率。

5. 数组配对问题的枚举算法设计与枚举对象的变化策略

任务描述:给定长度为n的数组A和一个正整数k,问从数组中任选两个数使其和为k的倍数,有多少中选择方法,对于数组a1 = 1, a2 = 2, a3 = 3而言:(a1, a2) 和 (a2, a1) 被认为是同一种选法,(a1, a2) 和 (a1, a3)被认为是不同选法。

输入:第一行有两个正整数 n, k,其中 n<=1000000, k<=1000 第二行有 n 个正整数,每个数的大小不超过 1e9。

输出:选出一对数使其和是k的倍数的选法个数。

分析过程:确定枚举对象为 obj(ai, aj) 1 <= i, j <= n。逐一列举注意验证,双层 for 循环。

算法优化:根据题目描述,取出的两个数是k的倍数,则有(ai + aj)%k = 0,可以推出 (ai%k + aj%k)%k = 0成立,因此可以对输入数据进行一个分组,其中对k取模后的值相同的放在同一组,同时用 bi 记录a%k = i组的数据个数,若第 i 组和第 j 组满足对k取模后的和是k的倍数,则属于这两组的数据均可以满足该条件,因此所计算的数目为 bi * bj,其中i, j取值范围为 0~k-1。根据优化或的思路,该算法可以用单层 for 循环表示,且循环的上限为 k 的大小,大大的提高了算法的运行效率。

6. 枚举算法的优化1(二分枚举法)

任务描述:给定长度为n的单调不下降数列{a0, ..., an-1} 和目标数k,求满足条件ai>=k的最小值 i。不存在的情况下输出n。

输入:n = 5, a = {2,3,3,5,6}, k=4;输出:3

分析过程:枚举对象 i,0 <= i <= n-1,一层 for循环,按照 i 从小到大遍历并逐一验证 ai >= k,时间复杂度为O(n)

 算法优化:二分法,每次和中间的元素比较,跨越式枚举,不断更新上界和下届找到目标值,时间复杂度为O(logn)。

7. 枚举算法的优化2

任务描述:有一条河,河中间有一些石头,石头的数量以及相邻两块石头之间的距离已知。现在可以移除一些石头,假设最多可以移除m块石头(注意:首尾两块石头不可以移除,且假定所有的石头都处于同一条直线)问最多移除m块石头后相邻两块石头之间的最小距离的最大值是多少。

输入:多组输入(<=20组数据,读入以EOF结尾),每组第一行输入两个数字:n(2<=n<=1000)为石头的个数,m(0<=m<=n-2)为可移除的石头数目,随后n-1个正整数,表示顺序相邻两个石头的距离d(d <= 1000)。

输出:每组输出一行结果,表示最大值。

分析过程:分析枚举对象,从 n 块石头中选择 m 块石头移走,由于m是动态的,因此构造 for 循环较困难,可以选择递归的方式,列举不定多元组的所有元素。该算法的时间复杂度为O(2^n)。

算法优化:将问题建立模型可以描述为求满足条件C(d)的最大值d,其中C(d)描述为C(d) = 最多移除m个石头后最近两个石头的距离不小于d,将其转化为求解最多移除m个石头后任意相邻两个石头的距离不小于d。可以应用贪心思想,首先循环依次考虑相邻石头,如果距离小于d,则去掉一个石头;如果任意相邻石头的距离都不小于d,则返回true;如果移除的石头个数大于m,则返回false,算法优化后的时间复杂度为O(nlogn)。

8. 小结

1. 枚举算法的基本原理

设计枚举对象,一一列举(for循环或递归),逐一验证。

2. 简单的枚举问题

模糊数字、n钱m鸡等问题

3. 枚举的优化方法

针对枚举对象的优化:

百钱百鸡问题:参数的独立性

数组配对:枚举对象的转换

针对枚举过程的优化:

二分枚举的一般模型

经典:二分查找,绳子切割和石头移除

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值