2024年春季学期《算法分析与设计》练习3

问题 A: 判断是否为2的幂

题目描述

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 0 ;否则,返回 1 。
如果存在一个整数 x 使得n == 2x ,则认为 n 是 2 的幂次方。
请用递归编写一个程序来判断。
【友情提醒:请使用递归来实现】
 

输入

多组输入,每组输入一个非负整数,每组占一行

输出

输出结果,每个结果占一行

样例输入 Copy
1
16
3
4
5
样例输出 Copy
0
0
1
0
1
#include<bits/stdc++.h>
using namespace std;
//循环
int dfs(int n){
	for(int i = 0;i <= 30;i++){
		if(n == pow(2,i))return 0;
	}
	return 1;
} 
int main(){
	int n;
	while(cin >> n){
		cout << dfs(n) << endl;
	}
	return 0;
}
//递归
#include<bits/stdc++.h>
using namespace std;

int dfs(int n){
    if(n == 1) return 0;
    if(n % 2 == 1) return 1;
    return dfs(n/2);
}

int main(){
    int n;
    while(cin >> n){
        cout << dfs(n) << endl;
    }
    return 0;
}

问题 B: 递归求和

题目描述

使用递归编写一个程序,求:
S(n)=1-1/2+1/3-1/4+1/5-1/6+......

输入

多组数据输入,每组输入一个正整数n。

输出

输出S(n)的计算结果(精确到小数点后六位)。

样例输入 Copy
1
样例输出 Copy
1.000000
//循环
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
double dfs(double n){
	double ans = 0;
	double res = 0;
	for(int i = 1;i <= n;i += 2){
		ans += 1.0 / i * 1.0;
	}
	for(int i = 2;i <= n;i += 2){
		res += 1.0 / i * 1.0;
	}
	return ans - res;
}
int main(){
	double n;
	while(cin >> n){
		cout << fixed << setprecision(6) << dfs(n) << endl;
	}
	return 0;
}
//递归
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

double dfs(double n, int i){
    if(i > n){
        return 0.0;
    }
    return 1.0/i - dfs(n, i+2);
}

int main(){
    double n;
    while(cin >> n){
        cout << fixed << setprecision(6) << dfs(n, 1) << endl;
    }
    return 0;
}

问题 C: 汉诺塔II

题目描述

用1,2,...,n表示n个盘子,称为1号盘,2号盘,...。号数大盘子就大。经典的汉诺塔问题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘。上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一回只能移动一个圆盘。我们知道最少需要移动2^64-1次.在移动过程中发现,有的圆盘移动次数多,有的少 。 告之盘子总数和盘号,计算该盘子的移动次数.

输入

包含多组数据,首先输入T,表示有T组数据.每个数据一行,是盘子的数目N(1<=N<=60)和盘号k(1<=k<=N)。

输出

对于每组数据,输出一个数,到达目标时k号盘需要的最少移动数。

样例输入 Copy
2
60 1
3 1
样例输出 Copy
576460752303423488
4

这个就是找规律

当只有1个盘子的时候,1号盘子移动1次;

当有2个盘子的时候,1号盘子要移动2次,2号盘子移动1次;

当有3个盘子的时候,1号盘子要移动4次,2号盘子移动2次,3号盘子移动1次;

以此类推:

当有n个盘子时,1号盘子移动2^(n-1),2号盘子移动2^(n-2),……,n号盘子移动2^(n-n)次;

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll dfs(int n){
	ll a = 1;
	for(int i = 1;i <= n;i++)a *= 2;
	return a;
} 
int main(){
	int t;
	cin >> t;
	while(t--){
		int n,m;
		cin >> n >> m;
		cout << dfs(n - m) << endl;
	}
	return 0;
}

问题 D: 九数组分数

题目描述

1, 2, 3...9 这九个数字组成一个分数,其值恰好为1/3,要求每个数字出现且只能出现一次,如何组合?编写程序输出所有的组合。

输入

输出

输出所有的结果,如果有多个,每条结果占一行。
结果的格式 : xxxx/xxxxx ,按照分子从小到大的顺序输出。

这个可以把除法换乘法   xxxx*3=xxxxx

#include<bits/stdc++.h>
using namespace std;
string s = "123456789";
int main(){
    int ans = 0;
	do{
		int res = stoi(s.substr(0,4));
		int sum = stoi(s.substr(4,9));
		if(res * 3 == sum)cout << res << "/" << sum << endl,ans++;
	}while(next_permutation(s.begin(),s.end()));
    // cout << ans << endl;
	return 0;
}

问题 E: 数的划分

题目描述

使用递归编写一个程序,求一个正整数n的所有划分个数。
例如,输入3,输出3;输入4,输出5。

输入

多组输入,每一组是一个正整数n。

输出

输出划分数。

样例输入 Copy
3
4
样例输出 Copy
3
5

这个题的意思就是3可以分为

3                                        4

2+1                                    3+1

1+1+1                                2+2    2+1+1

 有3个                               1+1+1

                                           有4个

所以我们就要用间接递归

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int dfs(int n,int m){
	if(n == 1 || m == 1)return 1;
	if(n < m)return dfs(n,n);
	if(n == m)return 1 + dfs(n,n - 1);
	if(n > m)return dfs(n,m - 1) + dfs(n - m,m);
}
	
int main(){
	int n;
	while(cin >> n){
		cout << dfs(n,n) << endl;
	}
	return 0;
}

问题 F: 字母全排列

题目描述

编写一个程序,使用递归算法输出一个一维字符数组中所有字符的全排列,假设字符都不一样。例如{'a','b','c'}的全排列为(a,b,c), (a,c,b), (b,a,c), (b,c,a), (c,a,b), (c,b,a)

输入

多组测试用例,每组输入一个正整数n(0<n<=26)。

输出

输出从a开始,连续n个字母的全排列,且每组输出之间用空格隔开。

样例输入 Copy
1
2
样例输出 Copy
a

ab
ba

这个比较简单,就是要我们手写一个next_permutation()的函数

//这个就是手写的next_permutation()
#include<bits/stdc++.h>
using namespace std;
void permute(vector<char>& chars, int start, int end) {
    if (start == end) {
        for (int i = 0; i <= end; ++i) {
            cout << chars[i];
        }
        std::cout << std::endl;
    } else {
        for (int i = start; i <= end; ++i) {
            swap(chars[start], chars[i]);
            permute(chars, start + 1, end);
            swap(chars[start], chars[i]);
        }
    }
}

int main() {
    int n;
    while (cin >> n) {
        vector<char> chars;
        for (int i = 0; i < n; ++i) {
            chars.push_back('a' + i);
        }

        permute(chars, 0, n - 1);
        cout << endl;
    }

    return 0;
}
//这个就是直接用next_permutation()
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	while(cin >> n){
	
	vector<char>arr;
	for(int i = 1;i <= n;i++){
		int ans = i - 1;
		char s = (char)(ans + int('a'));
		arr.push_back(s);
	}
	do{
		for(auto i : arr){
			cout << i;
		}
		cout << endl;
	}while(next_permutation(arr.begin(),arr.end()));
	cout << endl;
}
	return 0;
}

问题 G: 高桥和低桥

题目描述

有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”。举例说明:
假定高桥和低桥的高度分别是5和2,初始水位为1
第一次洪水:水位提高到6(两个桥都被淹),退到2(高桥不再被淹,但低桥仍然被淹)
第二次洪水:水位提高到8(高桥又被淹了),退到3。
没错,文字游戏。关键在于“又”的含义。如果某次洪水退去之后一座桥仍然被淹,那么下次洪水来临水位提高时不能算“又”淹一次。
输入n座桥的高度以及第i次洪水的涨水水位ai和退水水位bi,统计有多少座桥至少被淹了k次。初始水位为1,且每次洪水的涨水水位一定大于上次洪水的退水水位。

输入

输入文件最多包含25组测试数据。每组数据第一行为三个整数n, m, k(1<=n,m,k<=10^5)。第二行为n个整数hi(2<=hi<=10^8),即各个桥的高度。以下m行每行包含两个整数ai和bi(1<=bi<ai<=10^8, ai>bi-1)。输入文件不超过5MB。

输出

对于每组数据,输出至少被淹k次的桥的个数。

样例输入 Copy
2 2 2
2 5
6 2
8 3
5 3 2
2 3 4 5 6
5 3
4 2
5 2
样例输出 Copy
Case 1: 1
Case 2: 3

这个就用二分法,也可以用离散,树状数组,线段树(本人这里只会二分>.<!)

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n , m , k;
    int t = 1;
    while(~scanf("%d%d%d",&n,&m,&k)){
        int a[n + 5],b[n + 5];
        memset(b,0,sizeof(b));
        for(int i = 0;i < n ;i++){
            scanf("%d",&a[i]);
        }
        sort(a, a + n);
        int sum = 1;
        for(int i = 0;i < m;i++){
            int h,v;
            scanf("%d%d",&h,&v);
            int ans = lower_bound(a,a + n,sum + 1) - a;
            int res = lower_bound(a,a + n,h + 1) - a;
            sum = v;
            b[ans]++;
            b[res]--;
        }
        int num = 0;
        int arr = 0;
        for(int i = 0;i < n;i++){
            num += b[i];
            if(k <= num)arr++;
        }
        printf("Case %d: %d\n",t++,arr); 
    }
 
    return 0;
} 

问题 H: 好老师

题目描述
我想当一个好老师,所以我决定记住所有学生的名字。可是不久以后我就放弃了,因为学生太多了,根本记不住。但是我不能让我的学生发现这一点,否则会很没面子。所以每次要叫学生的名字时,我会引用离他最近的,我认得的学生。比如有10个学生:
A ? ? D ? ? ? H ? ?

想叫每个学生时,具体的叫法是: 

 
输入

输入只有一组数据。第一行是学生数n(1<=n<=100)。第二行是每个学生的名字,按照从左到右的顺序给出,以逗号分隔。每个名字要么是不超过3个英文字母,要么是问号。至少有一个学生的名字不是问号。下一行是询问的个数q(1<=q<=100)。每组数据包含一个整数p(1<=p<=n),即要叫的学生所在的位置(左数第一个是位置1)。

输出

对于每个询问,输出叫法。注意"middle of X and Y"只有当被叫者离有两个最近的已知学生X和Y,并且X在Y的左边。

样例输入 Copy
<span style="background-color:#ffffff"><span style="color:#333333"><span style="background-color:#ffffff"><span style="color:#333333"><span style="background-color:#f5f5f5">10
A ? ? D ? ? ? H ? ?
4
3
8
6
10</span></span></span></span></span>
样例输出 Copy
<span style="background-color:#ffffff"><span style="color:#333333"><span style="background-color:#ffffff"><span style="color:#333333"><span style="background-color:#f5f5f5">left of D
H
middle of D and H
right of right of H</span></span></span></span></span>

 不用用结构体就只能过一半,我也不知道怎么回事>.<

#include<bits/stdc++.h>
using namespace std;
struct m{
	string arr;
}s[150];
int main(){
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++)cin >> s[i].arr;
	int t;
	cin >> t;
	while(t--){
		int a,t = -10,b = 1,c = 0;
		cin >> a;
		for(;t < 0;b++){
			if(s[a].arr != "?"){
				t = a;
				break;
			}else {
				if((a - b) > 0){
					if(s[a - b].arr != "?"){
						t = a - b;
						c++;
					}
				}
				if((a + b) <= n){
					if(s[a + b].arr !=  "?"){
						t = a + b;
						c++;
					}
				}
			}
			if(c != 0)break;
		}
		if(c == 2) cout << "middle of " << s[a - b].arr << " and " << s[a + b].arr;
		if(c == 1){
			if(t == a + b){
				for(int i = 1;i <= b - 1;i++){
					cout << "left of ";
				}
				cout << "left of " << s[a + b].arr;
			}
			if(t == a - b){
				for(int i = 1;i <= b - 1;i++){
					cout << "right of ";
				}
				cout << "right of " << s[a - b].arr;
			}
		}
		if(c == 0)cout << s[a].arr;
		cout << endl;
	}
	return 0;
}

  • 29
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值