杭电oj(2097、2098、2099、1995、1996、2064、2077、2175)题解

 这次的题大都是有关汉罗塔问题的,前几题并不是很难,找规律即可,最后一题有些难了,让计算第n次移动盘子的编号。想了好久才写出来。

目录

问题描述

解决思路

移动次数规律

2097

题目

思路

代码

2098

题目

思路

整体思路

代码

2099

题目

思路

整体思路

代码

1995

题目

规律

代码

1996

题目

规律

代码

2064

题目

规律

代码

2077

题目

规律

代码

2075

题目

思路

代码


在此之前可以了解一下什么是汉罗塔问题,其实就是递归。下面是汉罗塔问题的模板可以进行借鉴。

/*
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 函数进行判断,并输出相应的结果。

  • 变量初始化
    • tempstempsl 和 tempse 分别用于存储原数,以便在不同进制转换过程中使用。
    • sumssumsl 和 sumse 分别用于存储十进制、十六进制和十二进制下各位数字的和,初始值都为 0。
  • 计算各位数字之和
    • 十进制:使用 while 循环,通过取模运算 % 得到当前数的最后一位数字,累加到 sums 中,然后通过整除运算 / 去掉最后一位数字,直到当前数变为 0。
    • 十六进制:同样使用 while 循环,取模运算 % 16 得到十六进制下的最后一位数字,累加到 sumsl 中,再通过整除运算 / 16 去掉最后一位数字,直到当前数变为 0。
    • 十二进制:使用 while 循环,取模运算 % 12 得到十二进制下的最后一位数字,累加到 sumse 中,再通过整除运算 / 12 去掉最后一位数字,直到当前数变为 0。
  • 判断是否为 “Sky Number”:如果 sumssumsl 和 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

题目

思路

整体思路

  1. 素数筛选:使用 prime 函数判断一个数是否为素数,再通过 ss 函数找出 2 到 10000 之间的所有素数,并将它们存储在向量 a 中。
  2. 输入处理:在 main 函数中,持续读取输入的整数,直到输入为 0 时停止。
  3. 组合计数:对于每个输入的非零整数 n,遍历素数向量 a,找出所有满足 a[i] + a[j] == n(其中 i < j)的组合,并统计组合的数量。
  4. 输出结果:将每种情况下的组合数量输出。
  • 功能:读取输入的整数,计算每个非零整数能表示成两个不同素数之和的组合数量,并输出结果。
  • 实现步骤
    • 调用 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

题目

思路

整体思路

  1. 输入处理:通过 while 循环持续读取整数对 (n, m),直到输入的 n 和 m 都为 0 时停止。
  2. 构建目标数范围:对于每一对输入的 (n, m),将 n 乘以 100 得到 num,这是为了给 n 后面留出两位数字的空间,后续会在这两位上进行遍历。
  3. 寻找满足条件的两位数:从 0 到 99 遍历所有可能的两位数 i,检查 num + i 是否能被 m 整除,如果能,则将 i 存入向量 a 中。
  4. 输出结果:遍历向量 a,将其中的每个两位数按要求输出。如果两位数小于 10,需要在前面补 0;如果不是最后一个数,在输出后要输出一个空格。
  5. 清理向量:每处理完一对输入,清空向量 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;
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值