王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序

2024-03-17 17:44

本文主要是介绍王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第 8 章 递归与分治

递归是指:函数直接或间接调用自身的一种方法,通常可把一个复杂的大型问题层层转化为与原问题相似但规模较小的问题来求解。
递归策略只需少量的程序就可描述解题过程所需的多次重复计算,因此大大减少了程序的代码量。

8.1 递归策略

构成递归需要具备两个条件:
1) 子问题必须与原始问题相同,且规模更小。
2) 不能无限制地调用本身,必须有一个递归出口。

例:n 的阶乘

题目描述:
输入一个整数 n ,输出 n 的阶乘(每组测试用例可能包含多组数据,请注意处理)。
输入: 一个整数 n 1 <=   n <=   20
输出: n 的阶乘
样例输入:
3
样例输出:
6
递归问题思路:
代码表示:
#include <bits/stdc++.h>
using namespace std;int Factorial(int n){if(n==1){return 1;}else{return Factorial(n-1)*n;}
}
int main() {int n;scanf("%d",&n);printf("%d\n", Factorial(n));return 0;
}

例:汉诺塔 III

题目描述:
在一块铜板上有三根杆,最左边的杆自上而下、由小到大顺序串着由 64 个圆盘构成的塔。目的是将最左边杆上的圆盘全部移到右边的杆上条件是一次只能移动一个圆盘,并且不允许大圆盘放在小圆盘的上面。
现在我们改变这个游戏的玩法:不允许直接从最左(右)边移动到最右(左)边(每次移动一定是移到中间杆或从中间杆移出),也不允许大圆盘放到小圆盘的上面。 现在有 N 个圆盘,她至少需要多少次移动才能把这些圆盘从最左边移到最右边?
输入: 包含多组数据,每次输入一个 N 值( 1 <=   N <=  35 )。
输出: 对于每组数据,输出移动最小的次数。
样例输入:
1
3
12
样例输出:
2
26
531440
代码表示:
#include <bits/stdc++.h>
using namespace std;
long long hanoi(int n){if(n==1){return 2;}else{return 3*hanoi(n-1)+2;}
}
int main() {int n;while (scanf("%d",&n)!=EOF){printf("%lld",hanoi(n));}return 0;
}

思路提示:

       若从第一根杆上移动 N 个圆盘到第三根杆上需要 F[N] 次移动,那么综上所述 F[N] 的组成方式 如下:先移动 N -1个圆盘到第三根杆上需要 F[N -1] 次移动,然后将最大的圆盘移动到中间杆上需要 1 次移动,再将 N -1个圆盘移动回第一根杆上同样需要 F[N-1] 次移动,移动最大的盘子到第三根杆上需要 1 次移动,最后将 N -1个圆盘移动到第三根杆上需要 F[N -1] 次移动,于是便有了 F[N] = 3F[N -1] + 2 。也就是说,从第一根杆上移动 N 个圆盘到第三根杆上,需要三次从第一根杆上移动 N -1个圆盘到第三根杆上,外加两次对最大圆盘的移动。这样就可以将移动 N 个圆盘的问题转换为问题相同但规模更小的移动 N -1个圆盘的问题。

        递归出口:当 N 1 时,即从第一根杆上移动一个盘子到第三根杆上,所需的移动次数显而易见为 2。移动一个盘子所需次数是最底层的子问题,该子问题不可继续分解且易于求解。这便是递归的出口。


8.2 分治法

分治法字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多个子问题,子问题之间互相独立且与原问题相同或相似。之后再把子问题分成更小的子问题,以此类推,直到最后的子问题可以简单地直接求解,原问题的解即子问题的解的合并。由于分治法产生的子问题往往与原问题相同且模式更小,这就为使用递归策略求解提供了条件。在这种情况下,通过反复利用分治手段,可以使子问题的规模不断缩小,最终缩小到可以直接求解的情况。在从过程中会导致了递归过程的产生。分治与递归就像一对孪生兄弟,经常同时应用在算法设计中,这也是将分治法和递归策略放在同一章中进行讲解的原因。分治法的步骤如下:
1. :将问题分解为规模更小的子问题。
2. :将这些规模更小的子问题逐个击破。
3. :将已解决的子问题合并,最终得出“母”问题的解

例:Fibonacci(上交大复试)

题目描述

斐波那契数列{0,1,1,2,3,5,8,13,21,34,55...}由递归定义: F0=0 F1=1 Fn=Fn-1+Fn-2,n>=2 编写一个程序来计算斐波那契数列。

输入描述:每个情况都包含一个数字 n,您应该计算 Fn.(0<=n<=30) 。

输出描述:对于每种情况,在单独的行上打印一个数字 Fn,这意味着第 n 个斐波那契数列.

输入:1

输出: 1
代码表示
#include <bits/stdc++.h>
using namespace std;
int feibo(int n){if(n==0){return 0;}else if(n==1){return 1;}else{return feibo(n-1)+feibo(n-2);}
}
int main() {int n;while (scanf("%d",&n)!=EOF){printf("%d",feibo(n));}return 0;
}

例:二叉树(北大上机)

题目描述:
如下所示,由正整数 1,2,3, …组成了一棵特殊二叉树。我们已知这棵二叉树的最后一个结点是 n 。 现在的问题是,结点 m 所在的子树中一共包括多少个结点。比如, n = 12 m = 3 ,那么上图中的结点 13, 14, 15 及后面的结点都是不存在的,结点 m 所在子树中包括的结点有 3, 6, 7, 12 ,因此结点 m 所在的子树中共有 4 个结点。
输入: 输入数据有多行,每行给出一组测试数据,包括两个整数 m n 1 <=   m <= n <=   1000000000 )。
输出: 对于每组测试数据,输出一行,该行包含一个整数,给出结点 m 所在子树中包括的结点的数目。
样例输入:
3 12
样例输出:
4
代码表示:
#include <bits/stdc++.h>
using namespace std;
int tree(int m,int n){if(m>n){return 0;//边界情况 }else{return 1+tree(2*m,n)+tree(2*m+1,n);}
}
int main() {int m,n;while (scanf("%d%d",&m,&n)!=EOF){if(m==0){break;}printf("%d",tree(m,n));}return 0;
}

[蓝桥杯 2015 省 B] 移动距离

题目描述

X 星球居民小区的楼房全是一样的,并且按矩阵样式排列。其楼房的编号为1,2,3,⋯ 。当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 6 时,开始情形如下:

1  2  3  4  5  6
12 11 10 9  8  7
13 14 15 .....

我们的问题是:已知了两个楼号 m 和 n,需要求出它们之间的最短移动距离。(不能斜线方向移动)

输入格式

输入为 3 个整数w,m,n,空格分开,都在 1到 10000 范围内。w 为排号宽度,m,n 为待计算的楼号。

输出格式

要求输出一个整数,表示 m 与 n 两楼间最短移动距离

代码表示
#include <bits/stdc++.h>
using namespace std;
int w,m,n,kx,ky,x=0,y=1;//m坐标x,y;n坐标kx,ky 
int main()
{scanf("%d%d%d",&w,&m,&n);if(n>m) swap(n,m);for(register int i=1;i<=m;i++)//枚举从1到m所有数的坐标{if(y%2==1)//正方向{x++;if(x>w) x=w,y++;}else    //反方向{x--;if(x<1) x=1,y++;}if(i==n) kx=x,ky=y;    //记录n的坐标}printf("%d",abs(kx-x)+abs(ky-y));return 0;
}
心得体会:

这个代码很巧妙,实际上所列出的数实际是什么根本就不重要,重要的是这个点所在坐标位置数的大小。以此来求解两坐标的距离。abs(kx - x)用于计算目标位置和当前位置在水平方向上的距离差的绝对值。


[蓝桥杯 2017 省 B] 日期问题

题目描述

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。比如 02/03/04,可能是 2002 年 03 月 04 日、2004 年 02 月 03 日或 2004 年 03 月 02 日。给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入格式

一个日期,格式是 AA/BB/CC。(0≤A,B,C≤9)

输出格式

输出若干个不相同的日期,每个日期一行,格式是 yyyy-MM-dd。多个日期按从早到晚排列。

#include <bits/stdc++.h>
using namespace std;char a[20];//存储输入的日期 
int M[20]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int main()
{int l,i,j,k,x,y,z;scanf("%s",a);l=strlen(a);//解析日期中的年份,日期,月份为整数 x=(a[0]-48)*10+a[1]-48;y=(a[3]-48)*10+a[4]-48; z=(a[6]-48)*10+a[7]-48;for(i=1960;i<=2059;i++){if(i%400==0||(i%4==0&&i%100!=0))	//判断闰年M[2]=29;//将二月的天数设为29 for(j=1;j<=12;j++)//遍历每个月 {for(k=1;k<=M[j];k++)//遍历每个月的每一天 
//这个i%100也就是遍历到某个四位数的年份取其后两位 if((x==i%100&&y==j&&z==k)||(z==i%100&&x==j&&y==k)||(z==i%100&&y==j&&x==k)){cout<<i<<"-";if(j<10)cout<<0;cout<<j<<"-";if(k<10)cout<<0;cout<<k<<endl;}	}M[2]=28;//别忘了 }
}
心得体会:

1、在ASCII编码中,数字字符'0'的ASCII码是48,而其后的字符依次递增。因此,通过将字符'a[0]'减去48,可以得到对应的数字值。在这段代码中,a[0]是输入日期字符串的第一个字符,例如'0'、'1'、'2'等。由于这些字符是ASCII码表中的字符表示形式,而我们需要得到的是其对应的数字值,所以需要将字符'0'的ASCII码值48减去。然后,将结果乘以10,以便处理两位数的年份。接下来,从字符'a[1]'中减去48,再将结果与之前的乘积相加,从而得到两位数的年份值。

2、这道题开始处解析日期的时候就很灵活,后续采用暴力循环,接着写if语句来取不同日期的值。

这篇关于王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/819674

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现