这次的题大都是有关汉罗塔问题的,前几题并不是很难,找规律即可,最后一题有些难了,让计算第n次移动盘子的编号。想了好久才写出来。
目录
在此之前可以了解一下什么是汉罗塔问题,其实就是递归。下面是汉罗塔问题的模板可以进行借鉴。
/*
hanoi模板
#include<iostream>
using namespace std;
void hanoi(int n,char a,char b,char c){
if(n==1) printf("%c->%c\n",a,c);
else{
hanoi(n-1,a,c,b);
printf("%c->%c\n",a,c);
hanoi(n-1,b,a,c);
}
}
int main(){
hanoi(3,'A','B','C');
return 0;
}
*/
汉诺塔问题是一个经典的递归问题,也被称为河内塔问题。
问题描述
- 有三根杆子,分别标记为 A、B、C。
- 最初,A 杆上有 N 个大小不同的圆盘,圆盘从下到上按大小顺序叠放,最大的圆盘在最下面,最小的圆盘在最上面。
- 要求将 A 杆上的所有圆盘移动到 C 杆上,在移动过程中,每次只能移动一个圆盘,且大盘不能放在小盘上面,可借助 B 杆作为辅助。
解决思路
- 当 N = 1 时,直接将圆盘从 A 杆移动到 C 杆即可。
- 当 N > 1 时,需要借助递归的思想来解决:
- 先将 A 杆上的 N - 1 个圆盘借助 C 杆移动到 B 杆上。
- 然后将 A 杆上剩下的最大圆盘移动到 C 杆上。
- 最后再将 B 杆上的 N - 1 个圆盘借助 A 杆移动到 C 杆上。
移动次数规律
- 移动 N 个圆盘所需的最少移动次数为 \(2^N - 1\) 次。例如,当 N = 3 时,需要移动 \(2^3 - 1 = 7\) 次。
汉诺塔问题在计算机科学、数学等领域有广泛的应用,它可以帮助人们理解递归算法的原理和应用,同时也是一个很好的思维训练题目。通过解决汉诺塔问题,能够锻炼逻辑思维能力和问题解决能力。
2097
题目
思路
“Sky Number” 指的是一个整数,它在十进制、十二进制和十六进制下各位数字之和相等。代码通过
sljz
函数来计算一个数在这三种进制下各位数字的和,然后比较这三个和是否相等,以此判断该数是否为 “Sky Number”。在main
函数中,程序会持续读取输入的整数,直到输入为 0 时停止,对每个输入的整数调用sljz
函数进行判断,并输出相应的结果。
- 变量初始化:
temps
、tempsl
和tempse
分别用于存储原数,以便在不同进制转换过程中使用。sums
、sumsl
和sumse
分别用于存储十进制、十六进制和十二进制下各位数字的和,初始值都为 0。- 计算各位数字之和:
- 十进制:使用
while
循环,通过取模运算%
得到当前数的最后一位数字,累加到sums
中,然后通过整除运算/
去掉最后一位数字,直到当前数变为 0。- 十六进制:同样使用
while
循环,取模运算% 16
得到十六进制下的最后一位数字,累加到sumsl
中,再通过整除运算/ 16
去掉最后一位数字,直到当前数变为 0。- 十二进制:使用
while
循环,取模运算% 12
得到十二进制下的最后一位数字,累加到sumse
中,再通过整除运算/ 12
去掉最后一位数字,直到当前数变为 0。- 判断是否为 “Sky Number”:如果
sums
、sumsl
和sumse
三者相等,则返回true
,表示该数是 “Sky Number”;否则返回false
。
代码
#include<iostream>
using namespace std;
bool sljz(int num){
int temps=num;
int tempsl=num;
int tempse=num;
int sums=0;
int sumsl=0;
int sumse=0;
while(temps>0){
int a=temps%10;
sums+=a;
temps/=10;
}
while(tempsl>0){
int a=tempsl%16;
sumsl+=a;
tempsl/=16;
}
while(tempse>0){
int a=tempse%12;
sumse+=a;
tempse/=12;
}
if(sums==sumsl && sumsl==sumse){
return true;
}
return false;
}
int main(){
int n;
while(cin>>n){
if(n==0) break;
if(sljz(n)) cout<<n<<" is a Sky Number."<<endl;
else if(!sljz(n)) cout<<n<<" is not a Sky Number."<<endl;
}
return 0;
}
2098
题目
思路
整体思路
- 素数筛选:使用
prime
函数判断一个数是否为素数,再通过ss
函数找出 2 到 10000 之间的所有素数,并将它们存储在向量a
中。- 输入处理:在
main
函数中,持续读取输入的整数,直到输入为 0 时停止。- 组合计数:对于每个输入的非零整数
n
,遍历素数向量a
,找出所有满足a[i] + a[j] == n
(其中i < j
)的组合,并统计组合的数量。- 输出结果:将每种情况下的组合数量输出。
- 功能:读取输入的整数,计算每个非零整数能表示成两个不同素数之和的组合数量,并输出结果。
- 实现步骤:
- 调用
ss
函数筛选出 2 到 10000 之间的所有素数。- 使用
while
循环持续读取输入的整数,直到输入为 0 时跳出循环。- 对于每个输入的非零整数
n
,初始化计数器ans
为 0。- 使用两层嵌套的
for
循环遍历素数向量a
,找出所有满足a[i] + a[j] == n
(其中i < j
)的组合,每找到一个组合,ans
加 1。- 输出
ans
的值,即该整数能表示成两个不同素数之和的组合数量。
代码
#include<iostream>
#include<vector>
using namespace std;
vector<int>a;
bool prime(int num){
if(num<=2) return false;
else{
for(int i=2;i*i<=num;i++){
if(num%i==0) return false;
}
}
return true;
}
void ss(){
for(int i=2;i<=10000;i++){
if(prime(i)) a.push_back(i);
}
}
int main(){
ss();
int n;
while(cin>>n){
if(n==0) break;
int ans=0;
for(int i=0;i<a.size();i++){
for(int j=i+1;j<a.size();j++){
if(a[i]+a[j]==n) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
2099
题目
思路
整体思路
- 输入处理:通过
while
循环持续读取整数对(n, m)
,直到输入的n
和m
都为 0 时停止。- 构建目标数范围:对于每一对输入的
(n, m)
,将n
乘以 100 得到num
,这是为了给n
后面留出两位数字的空间,后续会在这两位上进行遍历。- 寻找满足条件的两位数:从 0 到 99 遍历所有可能的两位数
i
,检查num + i
是否能被m
整除,如果能,则将i
存入向量a
中。- 输出结果:遍历向量
a
,将其中的每个两位数按要求输出。如果两位数小于 10,需要在前面补 0;如果不是最后一个数,在输出后要输出一个空格。- 清理向量:每处理完一对输入,清空向量
a
,以便处理下一对输入。
代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n,m;
while(cin>>n>>m){
vector<int>a;
if(n==0 && m==0) break;
int num=n*100;
for(int i=0;i<100;i++){
if((num+i)%m==0) a.push_back(i);
}
for(int i=0;i<a.size();i++){
if(a[i]<10) cout<<"0"<<a[i];
else cout<<a[i];
if(i!=a.size()-1) cout<<" ";
}
a.clear();
}
return 0;
}
从此处开始后面全是汉罗塔问题
注:汉罗塔问题不能使用pow函数去写
1995
题目
规律
N*pow(2,N-k)
代码
#include<iostream>
#include<math.h>
using namespace std;
#define int long long
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int num,cnt;
cin>>num>>cnt;
int sum=1;
for(int i=0;i<num-cnt;i++){
sum*=2;
}
cout<<sum;
}
return 0;
}
1996
题目
规律
pow(3,N)
代码
#include<iostream>
using namespace std;
#define int long long
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int num=0,sum=1;
cin>>num;
for(int j=0;j<num;j++){
sum*=3;
}
cout<<sum<<endl;
}
return 0;
}
2064
题目
规律
pow(3,N)-1
代码
#include<iostream>
using namespace std;
#define int long long
signed main(){
int n;
while(cin>>n){
int sum=1;
for(int i=0;i<n;i++){
sum*=3;
}
cout<<sum-1<<endl;
}
return 0;
}
2077
题目
规律
pow(3,N-1)+1
代码
#include<iostream>
using namespace std;
#define int long long
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int num,sum=1;
cin>>num;
for(int i=0;i<num-1;i++){
sum*=3;
}
cout<<sum+1<<endl;
}
return 0;
}
2175
题目
思路
思路很简单,就是判断第n次对应的盘子序号即可,不过容易超时。
代码借助递归函数
findPlate
来计算第mm
次移动的盘子编号。在main
函数里,持续读取输入的nn
(盘子总数)和mm
(移动次数),直到输入为0 0
时停止,每次输入后调用findPlate
函数计算并输出结果。
- 递归终止条件:
- 当
n
等于 1 时,意味着只有一个盘子,此时第mm
次移动的盘子编号必然是 1,所以直接返回 1。- 计算中间位置:
int mid = (1LL << (n - 1));
这里通过左移运算符<<
计算2^(n - 1)
,也就是把最大的盘子从起始柱移动到目标柱的移动次数。- 不同情况判断:
- 情况一:
move == mid
:若第mm
次移动恰好是最大盘子的移动,那么移动的盘子编号就是n
,直接返回n
。- 情况二:
move < mid
:这表明第mm
次移动是在将上面n - 1
个盘子从起始柱移动到辅助柱的过程中,所以递归调用findPlate(n - 1, move)
继续在n - 1
个盘子的移动中查找。- 情况三:
move > mid
:说明第mm
次移动是在将上面n - 1
个盘子从辅助柱移动到目标柱的过程中,此时相当于从第move - mid
次开始移动n - 1
个盘子,因此递归调用findPlate(n - 1, move - mid)
。
代码
#include <iostream>
#include <cmath>
using namespace std;
#define int long long
int nn, mm;
// 计算第 mm 次移动的盘子编号
int findPlate(int n, int move) {
if (n == 1) {
return 1;
}
int mid = (1LL << (n - 1));
if (move == mid) {
return n;
} else if (move < mid) {
return findPlate(n - 1, move);
} else {
return findPlate(n - 1, move - mid);
}
}
signed main() {
while (cin >> nn >> mm) {
if (nn == 0 && mm == 0) break;
cout << findPlate(nn, mm) << endl;
}
return 0;
}