算法竞赛入门经典授课教案第章暴力求解法.docx
《算法竞赛入门经典授课教案第章暴力求解法.docx》由会员分享,可在线阅读,更多相关《算法竞赛入门经典授课教案第章暴力求解法.docx(44页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、精品名师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名师归纳总结【教学内容相关章节】第 7 章暴力求解法可编辑资料 - - - 欢迎下载精品名师归纳总结7.1 简洁枚举7.2枚举排列7.3子集生成7.4 回溯法7.5隐式图搜寻【教学目标 】( 1)把握整数、子串等简洁对象的枚举方法。( 2)娴熟把握排列生成的递归方法。( 3)娴熟把握用“下一个排列”枚举全排列的方法。( 4)懂得解答树,并能估算典型解答树的结点数。( 5)娴熟把握子集生成的增量法、位向量法和二进制法。( 6)娴熟把握回溯法框架,并能懂得为什么它往往比生成
2、- 测试法高效。( 7)娴熟把握解答树的宽度优先搜寻和迭代加深搜寻。( 8)懂得倒水问题的状态图与八皇后问题的解答树的本质区分。( 9)娴熟把握八数码问题的BFS实现。( 10)娴熟把握集合的两种典型实现hash 表和 STL 集合。【教学要求 】把握整数、 子串等简洁对象的枚举方法。娴熟把握排列生成的递归方法。娴熟把握用 “下一个排列” 枚举全排列的方法。懂得子集树和排列树。娴熟把握回溯法框架。娴熟把握解答树的宽度优先搜寻和迭代搜寻。娴熟把握集合的两种典型实现hash 表和 STL 集合。【教学内容提要】本章主要争论暴力法(也叫穷举法、蛮力法),它要求调设计者找出全部可能的方法,然后挑选其中
3、的一种方法,如该方法不行行就摸索下一种可能的方法。介绍了排列生成的递归方法。在求解的过程中,提出明白答树的概念(如子集树和排列树)。介绍了回溯法的基本框架。介绍了集合的两种典型实现hash 表和 STL 集合。【教学重点、难点】教学重点:( 1)娴熟把握排列生成的递归方法。( 2)懂得解答树,并能估算典型解答树的结点数。( 3)娴熟把握子集生成的增量法、位向量法和二进制法。( 4)娴熟把握回溯法框架,并能懂得为什么它往往比生成- 测试法高效。( 5)娴熟把握解答树的宽度优先搜寻和迭代搜寻。( 6)娴熟把握集合的两种典型实现hash 表和 STL 集合。教学难点:( 1)娴熟把握子集生成的增量法
4、、位向量法和二进制法。( 2)娴熟把握回溯法框架,并能懂得为什么它往往比生成- 测试法高效。( 3)娴熟把握解答树的宽度优先搜寻和迭代搜寻。( 4)娴熟把握集合的两种典型实现hash 表和 STL 集合。【课时支配 】7.1简洁枚举7.2枚举排列7.3子集生成7.4回溯法7.5隐式图搜寻可编辑资料 - - - 欢迎下载精品名师归纳总结学习资料 名师精选 - - - - - - - - - -第 1 页,共 22 页 - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -引言暴力法也称为穷举
5、法、蛮力法, 它要求调设计者找出全部可能的方法,然后挑选其中的一种方法,如该方法不行行就摸索下一种可能的方法。暴力法也是一种直接解决问题的方法,经常直接基于问题的描述和所涉及的概念定义。暴力法不是一个最好的算法,但当我们想不出更好的方法时,它也是一种有效的解决问题的方法。暴力法的优点是规律清楚,编写程序简洁。在程序设计竞赛时,时间紧急,相对于高效的、奇妙的算法,暴力法编写的程序简洁,能更快的解决问题。同时蛮力法也是许多算法的基础,可以在蛮力法的基础上加以优化,得到更高效的算法。而且, 某些情形下, 算法规模不大,使用优化的算法没有必要,而且某些优化算法本身较复杂,在规模不在时可能由于复杂的算法
6、铺张时间,反而不如简洁的暴力搜寻。使用暴力法常用如下几种情形:( 1)搜寻全部的解空间。( 2)搜寻全部的路径。( 3)直接运算。( 4)模拟和仿真。7.1简单枚举在枚举复杂对象之前,先尝试着枚举一些相对简洁的东西,如整数、子串等。暴力枚举对问题进行肯定的分析往往会让算法更加简洁、高效。7.1.1 除法输入正整数n,按从小到大的次序输出全部形如abcde/fghij=n的表达式,其中a j恰好为数字0 9 的一个排列, 2 n79。样例输入:62样例输出:79546/01283=6294736/01528=62【分析】只需要枚举fghij就可以运算出abcde,然后判定是否全部数字都不相同即可
7、。不仅程 序简洁,而枚举量也从10.=3628800降低至不到1 万。由此可见,即使采纳暴力枚举,也是需要仔细分析问题。完整的程序如下:#include using namespace std;bool testint i,int j /用数组 t 存放 i , j 的各位数字int t10=0; /初始化数组t ,使得各位数字为0,好处是使得fghij10000时 f 位置为 0int ia = 0;whilei /取 i 中各位数字存放在数组t 中tia+ = i % 10; i = i / 10;whilej /取 j 中各位数字存放在数组t 中tia+ = j % 10; j = j
8、/ 10;/ 判定 aj 是否恰好为数字的0 9 的一个排列可编辑资料 - - - 欢迎下载精品名师归纳总结学习资料 名师精选 - - - - - - - - - -第 2 页,共 22 页 - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -forint m = 0; m 10; +m forint n = m+1; n n & n =2 & n = 79 k = 1234;whilek = 98765 int j = k * n;ifj 100000 /如 fghij10000,满意
9、题目的条件,f 位置输出0iftestj,kcout j / ;ifk 10000 cout 0; cout k = n endl;+k;return 0;7.1.2 最大乘积输入 n 个元素组成的序列S,你需要找出一个乘积最大的连续子序列。假如这个最大的乘积不是正整,应输出-1 (表示无解) 。1 n 18, -10 Si 10。样例输入:32 4 -352 5 -1 2 -1样例输出:820【分析】18连续子序列有两个要素:起点和终点, 因此只需要枚举起点和终点即可。由于每个元素的确定值不超过10,一共又不超过18 个元素,最大可能的乘积示会超过10 ,可以用 longlong 存下。完整
10、的程序如下:#include int mainint a20;/存放输入序列的每一个元素 int64 ans = 0; /存放最大的乘积int n;/输入元素的个数 int64 tmp。/存放乘积的中间结果whilescanf%d,&n.=EOF/输入序列中元素的个数n forint i = 0;i n; i+/输入序列中的元素scanf%d,&ai; fori = 0; i n; i+ 可编辑资料 - - - 欢迎下载精品名师归纳总结学习资料 名师精选 - - - - - - - - - -第 3 页,共 22 页 - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名
11、师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -/ 从序列中的第0 个元素开头,枚举最大连续乘积,并用ans 储备tmp = 1;forint j = i; j ansans = tmp;ifans0 printf%I64dn,ans;elseprintf%dn,-1;return 0;7.1.3 分数拆分输入正整数k,找到全部的正整数x y,使得 111 。kxy样例输入:212样例输出:21/2=1/6+1/31/2=1/4+1/481/12=1/156+1/131/12=1/84+1/141/12=1/60+1/151/12=1/48+1/161/12
12、=1/36+1/181/12=1/30+1/201/12=1/28+1/211/12=1/24+1/24【分析】找出全部有x,y,枚举完成了就行了。但是从1/12=1/156+1/13可以看出, x 可以比 y可编辑资料 - - - 欢迎下载精品名师归纳总结大许多。由于x y,有 11xy111,因此kyy,即 y 2k。这样,只需要在2k 范畴内枚可编辑资料 - - - 欢迎下载精品名师归纳总结举 y,然后依据y 尝试运算出x 即可。完整的程序如下:#include int mainint k;int x, y, count = 0;/变量 count统计等式的个数whilescanf%d,
13、 &k .= EOF fory = k+1;y = 2 * k; y+ /判定 1/k=1/x+1/y等式的个数,x=k * y / y - k;ifx * y-k = k * y count+;可编辑资料 - - - 欢迎下载精品名师归纳总结学习资料 名师精选 - - - - - - - - - -第 4 页,共 22 页 - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -printf%dn,count;/输出满意条件的等式的个数fory = k+1; y = 2 * k; y+ /
14、输出满意条件的等式x=k * y / y - k;ifx * y - k = k * y printf1/%d=1/%d+1/%dn,k,x,y;return 0;7.1.4 双基回文数假如一个正整数n 至少有两个不同的进位制b1 和 b2 下都是回文数 ( 2 b1,b 2 10),就称 n 是双基回文数(留意,回文数不以包含前导零)。输入正整数S106,输出比S 大的最小双基回文数。样例输入: 1600000样例输出: 1632995【分析】最自然的想法是:从 n+1 开头依次判定每个数是否为双基回文数,而在判定时要枚举所6可编辑资料 - - - 欢迎下载精品名师归纳总结有可能的基数(2
15、10)。意外的是:对于S10回文数太多太密。完整的程序如下:#include using namespace std;的“小规模数据”来说是足够快的双基可编辑资料 - - - 欢迎下载精品名师归纳总结bool huiwenint n int i,j,a30,s;int total = 0; /存放数 s 是回文数的基数个数forint base = 2; base = 10; base+ i = 1;s = n; whiles ai = s % base; s = s / base; i+;i-;forj = 1; j i/2total+; /数 s 在基 base 下是回文数,就total+
16、 iftotal = 2 return true;return false;int main int s;whilescanf%d,&s = 1 fors = s+1; ; s+ ifhuiwens cout s endl;break;可编辑资料 - - - 欢迎下载精品名师归纳总结学习资料 名师精选 - - - - - - - - - -第 5 页,共 22 页 - - - - - - - - - -可编辑资料 - - - 欢迎下载精品名师归纳总结资料word 精心总结归纳 - - - - - - - - - - - -return 0;7.2枚举排列输入整数 n,按字典序从大到小的次序输出
17、前n 个数的全部排列。两个序列的字典序大 小关系等价于从头开头第一个不相同位置处的大小关系。例如,1,3,22,1,3,字典序最小的排列是 1,2,3,4,n ,最大的排列是n,n-1,n-2,1 。n=3 时,全部排列的排序结果是: 1,2,3、1,3,2、2,1,3、2,3,1、 3,1,2、3,2,17.2.1 生成 1 n 的排列对此问题用递归的思想解决:先输出全部以1 开头的排列(递归调用),然后输出以2开头的排列(递归调用),接着以3 开头的排列,最终才是以n 开头的排列。以 1 开头的排列的特点是:第一位是1,后面是按字典序的2 9 的排列。所以在设计递归函数需要以下参数:( 1
18、)已经确定的“前缀”序列,以便输出。( 2)需要进行全排列的元素集合,以便依次选做第一个元素。这样,写出以下的伪代码:void print_permutation序列 A,集合 SifSelse为空 输出序列依据 从小到大次序A;依次考虑S 的每个元素vprint_permutation在 A 的末尾填加v 后得到的新序列,S-v;上面的递归函数中递归边界是S 为空的情形,可以直接输出序列A。如 S 不为空,就按从小到大的次序考虑S 中的每个元素,每次递归调用以A 开头。下面考虑程序的实现。用数组表示序列A,集合 S 可以由序列A 完全确定 A 中没有显现的元素都可以选。C 语言中的函数在接受
19、数组参数时无法得到数组的元素个数,所以需要传一个已经填好的位置个数,或者当前需要确定的元素位置cur 。声明一个足够大的数组A,然后调用print_permutationn,A,0,即可按字典序输出1 n 的全部排列。完整的程序如下:#include int A100;/输出 1 n 的全排列的递归函数void print_permutationint n, int* A, int cur int i, j;ifcur = n /递归边界fori = 0; i n; i+ printf%d , Ai; printfn;else fori = 1; i = n; i+ /尝试在 Acur中填各种
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法竞赛入门经典授课教案第章暴力求解法 算法 竞赛 入门 经典 授课 教案 暴力 解法
限制150内