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

记录下自己的学习记录。


问题A:超级青蛙

题目描述

一只超级青蛙一次可以跳上1级台阶,也可以跳上2级……它也能够跳上n级台阶。请问,该青蛙跳上一个n级的台阶总共有多少种跳法?

输入

输入一个正整数n表示台阶的数量。

输出

输出总的跳法数。

样例输入:
1
2
样例输出:
1
2

递推公式为F(n)=2*F(n-1)。


def jumpFloorII(number):
    if number <= 0:
        return 0
    if number == 1:
        return 1
    else:
        return 2 * jumpFloorII(number - 1)


while True:
    try:
        print(jumpFloorII(int(input())))
    except:
        break

问题B:Kimi数列

题目描述

Kimi最近在研究一个数列:

  • F(0) = 7
  • F(1) = 11
  • F(n) = F(n-1) + F(n-2) (n≥2) Kimi称之为Kimi数列。请你帮忙确认一下数列中第n个数是否是3的倍数。

输入

输入包含多组数据。 每组数据包含一个整数n,(0≤n≤30)。

输出

对应每一组输入有一行输出。 如果F(n)是3的倍数,则输出“Yes”;否则输出“No”。

样例输入:
0
1
2
3
4
5
样例输出:
No
No
Yes
No
No
No

简单递归


f = [7, 11]
for i in range(2, 32):
    f.append(f[i - 1] + f[i - 2])
while True:
    try:
        n = int(input())
        if f[n] % 3 == 0:
            print('Yes')
        else:
            print('No')
    except:
        break

问题C:汉诺塔

题目描述

使用递归编写一个程序实现汉诺塔问题,要求在输入圆盘数量之后,输出圆盘的移动步骤,输出格式示例如下: 第1步:1号盘从A柱移至B柱
第2步:2号盘从A柱移至C柱

输入

多组测试用例,每组输入一个正整数n,n代表圆盘数量。

输出

每组输出之间有一行空行。

样例输入:
3
样例输出:
第1步:1号盘从A柱移至C柱
第2步:2号盘从A柱移至B柱
第3步:1号盘从C柱移至B柱
第4步:3号盘从A柱移至C柱
第5步:1号盘从B柱移至A柱
第6步:2号盘从B柱移至C柱
第7步:1号盘从A柱移至C柱

题目不难但是一定要注意输出之间还要有一个换行!!!,用一个字典存储了每个盘子的编号。


def move(idd, s1, s2):
   global i
   i += 1
   if i == nn:
       print(f"第{
      
      i}步:{
      
      idd}号盘从{
      
      s1}柱移至{
      
      s2}柱")
   else:
       print(f"第{
      
      i}步:{
      
      idd}号盘从{
      
      s1}柱移至{
      
      s2}柱 ")


def hanoi(n, a, b, c):
   if n == 1:
       move(dist[n], a, c)
   else:
       hanoi(n-1, a, c, b)
       move(dist[n], a, c)
       hanoi(n-1, b, a, c)


while True:
   try:
       n = int(input())
       nn = 2**n-1
       dist = {
    
    }
       c = 1
       i = 0
       for k in range(1, n+1):
           if k not in dist:
               dist[k] = c
               c += 1
       hanoi(n, 'A', 'B', 'C')
       print()
   except:
       break

问题D:汉诺塔II

题目描述

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

输入

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

输出

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

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

1号盘子走2**(n-1)步,2号盘子走2**(n-2)步,3号盘子走2**(n-3)步,…,依次递推就行。

def check(x):
    step = 1
    for i in range(1, x+1):
        step *= 2
    return step


t = int(input())
for _ in range(t):
    n, k = map(int, input().split())
    print(check(n-k))
    

问题E:字母全排列

题目描述

编写一个程序,使用递归算法输出一个一维字符数组中所有字符的全排列,假设字符都不一样。例如{‘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个字母的全排列,且每组输出之间用空格隔开。

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

ab
ba

from itertools import *


while True:
    try:
        n = int(input())
        a = [] # 装n个英文字母
        s = 96  # a为97
        for _ in range(n):
            s += 1
            a.append(chr(s))
        for items in permutations(a):  # python中的库函数,返回一个按字典序排列的元组
            for j in items:
                print(j, end='')
            print()
        print()  # 注意输出要求,换行
    except:
        break


问题F:九数组分数

题目描述

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

输入

输出

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

from itertools import permutations

# 生成所有9个数字的排列
digits = permutations("123456789")

# 遍历所有排列,计算分数值
for d in digits:
    numerator = int("".join(d[:4]))
    denominator = int("".join(d[4:]))
    if numerator*3 == denominator:
        print("{}/{}".format(numerator, denominator))

问题G:高桥和低桥

题目描述

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

输入

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

输出

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

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

  • 将桥按照高度从小到大排序(便于二分优化)
  • 对于每个洪水,从高到低依次检查每座桥是否被淹,是的话加一。这里可以用二分查找来减少遍历次数,不然时间复杂度太高了(因为我们第一步就是排序)。
  • 输出被淹至少k次的桥的数量。
  • python版本还是过不了(50%)时间和内存爆了,C++可以过。

python:

from bisect import *

case = 0
while True:
    try:
        n, m, k = map(int, input().split())
        a = list(map(int, input().split()))  # 每座桥的高度
        a.sort()
        c = []  # 洪水的张水水位和退水水位
        num = [0] * (n+1)  # 桥被淹了多少次
        for _ in range(m):
            x, y = map(int, input().split())
            c.append([x, y])
        t = 0
        for item in c:
            left = bisect_left(a, t+1)  # 由题意可知如果退水位和桥高度一样的话不算淹的,所以加1
            right = bisect_right(a, item[0])
            t = item[1]
            num[left] += 1
            num[right] -= 1
        s = 0
        count = 0
        for i in range(n):
            s += num[i]
            if s >= k:
                count += 1
        case += 1
        print(f"Case {
      
      case}: {
      
      count}")
    except:
        break

C++:

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    
    
    int caseCnt = 0;
    while (true) {
    
    
        int n, m, k;
        if (scanf("%d%d%d", &n, &m, &k) != 3) {
    
    
            break;
        }
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
    
    
            scanf("%d", &a[i]);
        }
        sort(a.begin(), a.end());
        vector<pair<int, int>> c(m);
        for (int i = 0; i < m; i++) {
    
    
            scanf("%d%d", &c[i].first, &c[i].second);
        }
        int t = 0;
        vector<int> num(n + 1);
        for (int i = 0; i < m; i++) {
    
    
            int left = lower_bound(a.begin(), a.end(), t + 1) - a.begin();
            int right = upper_bound(a.begin(), a.end(), c[i].first) - a.begin() - 1;
            t = c[i].second;
            num[left] += 1;
            num[right + 1] -= 1;
        }
        int s = 0, count = 0;
        for (int i = 0; i < n; i++) {
    
    
            s += num[i];
            if (s >= k) {
    
    
                count += 1;
            }
        }
        caseCnt++;
        printf("Case %d: %d\n", caseCnt, count);
    }
    return 0;
}
 

问题H:汉诺塔III

题目描述

“汉诺塔”是一个众所周知的古老游戏。
现在我们把问题稍微改变一下:如果一共有4根柱子,而不是3根,那么至少需要移动盘子多少次,才能把所有的盘子从第1根柱子移动到第4根柱子上呢?

为了编程方便,您只需要输出这个结果mod 10000的值。

输入

该题含有多组测试数据,每组一个正整数n。(0<n<=50000)

输出

一个正整数,表示把n个盘子从第1根柱子移动到第4根柱子需要的最少移动次数mod 10000的值。

样例输入 :
15
样例输出 :
129

找规律(慢慢品)
引用博客:4个汉诺塔

在这里插入图片描述

num = [0] * 50011
num[1] = 1
cnt, s, limit = 0, 2, 2  # 统计个数,前后差值, 个数上限
for i in range(2, 50010):
   cnt += 1
   num[i] = (num[i - 1] + s) % 10000
   if cnt == limit:  # 当cnt=limit时,差值乘2模10000,cnt重置为0,limit加1
       cnt = 0
       limit += 1
       s *= 2
       s %= 10000

while True:
   n = int(input())
   print(num[n])

总结

冲就完了

猜你喜欢

转载自blog.csdn.net/weixin_62988040/article/details/129559891