HNUCM-2022年秋季学期《算法分析与设计》练习16

目录

问题 A: 递归求和

问题 B: 又一道简单题 

问题 C: 油田问题

问题 D: 文件存储

问题 E: 图的m着色问题

问题 F: 素数环

问题 G: 迷宫问题(回溯法求解)

问题 H: X星人的基因


问题 A: 递归求和

题目描述

使用递归编写一个程序求如下表达式的计算结果:  (1<n<=20)
S(n) = 1*4 + 4*9 + 9*16 + 16*25 + ... + ((n-1)^2)*n^2
输入n,输出表达式S(n)的结果。

输入

单组输入,输入一个正整数n,1<n<=20。

输出

输出表达式S(n)的计算结果。

思路:简单的递归题,题目已经给出了递归的公式,这里没使用递归

n = int(input())
a, b, res = 1, 4, 0
for i in range(0, n - 1):
    res, a, b = res + a * b, b, (i + 3) ** 2
    # print(a, b)
print(res)

问题 B: 又一道简单题 

题目描述

输入一个四个数字组成的整数 n,你的任务是数一数有多少种方法,恰好修改一个数字,把它 变成一个完全平方数(不能把首位修改成 0)。比如 n=7844,有两种方法:3844=622 和 7744=882。 

输入

输入第一行为整数 T (1<=T<=1000),即测试数据的组数,以后每行包含一个整数 n (1000<=n<=9999)。 

输出

对于每组数据,输出恰好修改一个数字,把 n变成完全平方数的方案数

思路:题目要求首位不能修改为0,也就意味着变动范围就四位数的范围,用计算机稍微计算一下四位数开平方的上下界,然后遍历这个范围内的整数,将它们的平方和n对比即可。

def check(x: int, y: int):
    tmp = 0
    while x > 0:
        tmp = tmp + 1 if x % 10 != y % 10 else tmp
        x, y = x // 10, y // 10
    if tmp == 1:
        return 1
    else:
        return 0


t, cnt = int(input()), 0
while t > cnt:
    cnt, n, res = cnt + 1, int(input()), 0
    for i in range(32, 100):
        res = res + check(i ** 2, n)
    print(f"Case {cnt}: {res}")

问题 C: 油田问题

题目描述

输入一个m行n列的字符矩阵,统计字符“@”组成多少个八连块。如果两个字符“@”所在的格子相邻(横、竖或者对角线方向),即属于同一个八连块。

输入

多组输入
输入行数m,以及列数n。
然后输入*和@
1<=n,m<=100

输出

联通块个数
思路:一道搜索题,因为是判断联通块的数量,所以注意不用回溯,利用深度优先搜索即可完成解题。
def dfs(x: int, y: int):
    q = [[x, y]]
    while q:
        tx, ty = q.pop(0)
        for d in directions:
            dx, dy = tx + d[0], ty + d[1]
            if 0 <= dx < m and 0 <= dy < n and maze[dx][dy] == '@' and visited[dx][dy] == 0:
                visited[dx][dy], _ = cnt, q.insert(0, [dx, dy])
    return


directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [1, -1], [-1, 1], [1, 1]]
while True:
    try:
        m, n = map(int, input().split())
        maze, visited, cnt = [input().strip() for _ in range(m)], [[0] * n for _ in range(m)], 0
        for i in range(m):
            for j in range(n):
                if maze[i][j] == '@' and visited[i][j] == 0:
                    visited[i][j], cnt = cnt + 1, cnt + 1
                    dfs(i, j)
        # print(visited)
        print(cnt)
    except:
        break

问题 D: 文件存储

题目描述

如果有n个文件{F1,F2,F3,…,Fn}需要存放在大小为M的U盘中,文件i的大小为Si,1<=i<=n。请设计一个算法来提供一个存储方案,使得U盘中存储的文件数量最多。

输入

多组输入,对于每组测试数据,每1行的第1个数字表示U盘的容量M(以MB为单位,不超过256*1000MB),第2个数字表示待存储的文件个数n。
第2行表示待存储的n个文件的大小(以MB为单位)。

输出

输出最多可以存放的文件个数。

思路:高情商:贪心法,低情商:排个序,从最小的文件开始收集,直到收不了为止。

while True:
    try:
        m, n = map(int, input().split())
        nums, cnt, res = [int(i) for i in input().split()], 0, 0
        nums.sort()
        for num in nums:
            if num + res <= m:
                cnt, res = cnt + 1, num + res
            else:
                break
        print(cnt)
    except:
        break

问题 E: 图的m着色问题

题目描述

 给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色,请输出着色方案。

输入

输入第一行包含n,m,k分别代表n个结点,m条边,k种颜色,接下来m行每行有2个数u,v表示u和v之间有一条无向边,可能出现自环边,所以请忽略自环边。

输出

输出所有不同的着色方案,且按照字典序从小到大输出方案。

思路:回溯法的经典问题,利用回溯法的思想解题即可。

def config(x: int, y: int):
    for i in range(1, x):
        if maze[i][x] == 1 and path[i - 1] == y:
            return True
    return False


def draw(x: int):
    if x > n:
        paths.append(path[:])
        return
    else:
        for i in range(1, k + 1):
            if not config(x, i):
                path.append(i)
                draw(x + 1)
                path.pop()
        return


while True:
    try:
        n, m, k = map(int, input().split())
        maze, paths, path = [[0] * (n + 1) for _ in range(n + 1)], [], []
        for _ in range(m):
            a, b = map(int, input().split())
            if a != b:
                maze[a][b] = maze[b][a] = 1
        draw(1)
        paths.sort()
        for path in paths:
            for j in range(len(path)):
                print(path[j], end='') if j == 0 else print('', path[j], end='')
            print()
    except:
        break

问题 F: 素数环

题目描述

现有1,2,3...,n,要求用这些数组成一个环,使得相邻的两个整数之和均为素数,要求你求出这些可能的环。

输入

输入正整数n。

输出

输出时从整数1开始逆时针输出,同一个环只输出一次,且满足条件的环应按照字典序从小到大输出。
注:每一个环都从1开始。

思路:回溯法的经典问题,利用回溯法的思想即可完成解题,回溯法的题目本质上就是剪枝函数和遍历方向不同。Tips:最后一位数插入之前,和当前path列表的首尾都要判断一次,因为题目要求的是一个环。

def check(index: int, x: int, y: int):
    tmp = path[x] + y
    for i in range(2, tmp // 2):
        if tmp % i == 0:
            return False
    if index == n:
        return True and check(-1, 0, y)
    return True


def walk(x: int):
    if x > n:
        paths.append(path[:])
        return
    else:
        for i in range(2, n + 1):
            if i not in path and check(x, -1, i):
                path.append(i)
                walk(x + 1)
                path.pop()
        return


while True:
    try:
        n, paths, path = int(input()), [], [1]
        walk(2)
        paths.sort()
        for path in paths:
            for j in range(len(path)):
                print(path[j], end='') if j == 0 else print('', path[j], end='')
            print()
    except:
        break

问题 G: 迷宫问题(回溯法求解)

题目描述

输入一个n×n的迷宫,定义左上角为起点,右下角为终点,寻找一条从起点到终点的路径

输入

多组输入
每组输入第一行有两个整数n,m表示迷宫尺寸
后跟n行,每行m个字符0表示道路,1表示墙壁
1<=n,m<=10

输出

输出地图,用2表示路径
多个答案输出任意一种即可

思路:经典的迷宫问题,修改一下剪枝函数和遍历方向即可。

def walk(x: int, y: int):
    if x == n - 1 and y == m - 1:
        paths.append(path[:])
        return
    else:
        for d in directions:
            dx, dy = x + d[0], y + d[1]
            if 0 <= dx < n and 0 <= dy < m and maze[dx][dy] == 0:
                maze[dx][dy], _ = 2, path.append((dx, dy))
                walk(dx, dy)
                maze[dx][dy], _ = 0, path.pop()
        return


directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
while True:
    try:
        n, m = map(int, input().split())
        maze, paths, path = [[int(i) for i in input().split()] for _ in range(n)], [], [(0, 0)]
        walk(0, 0)
        for i, j in paths[0]:
            maze[i][j] = 2
        for i in range(n):
            for j in range(m):
                print('', maze[i][j], end='') if j != 0 else print(maze[i][j], end='')
            print()
    except:
        break

问题 H: X星人的基因

题目描述

X星人的基因由A、B、C、D、E五种不同的结构组合而成。
如果两个性别不同的X星人的基因序列相似度大于50%,按照X星的法律他们是禁止结婚的,等于50%据说还是可以的。
那么基因的相似度怎么计算呢?分别从两个人身上取长度均为N的基因片段,如果它们的最长公共子序列为M,则相似度=M/N。是不是很简单呢?
现在给你两段X星人的基因序列片段,请你判断他们是不是可以结婚?

输入

每一组测试数据包含3行,
第1行数字N表示待比较基因序列片段的长度,N<=10^3。
第2行和第3行为两个长度为N的基因序列片段。
输入0表示结束。

输出

两个X星人是否可以结婚,如果可以输出”Yes“,如果不可以输出”No“。

思路:最长公共子序列的题目,按模板写即可,这里使用了滚动数组,不使用滚动数组也可。

while True:
    try:
        n = int(input())
        DNA1, DNA2, dp = "".join(input().split()), "".join(input().split()), [[0] * (n + 1) for _ in range(2)]
        # print(DNA1, DNA2)
        for i in range(1, n + 1):
            for j in range(1, n + 1):
                dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1 if DNA1[i - 1] == DNA2[j - 1] else max(dp[(i - 1) % 2][j], dp[i % 2][j - 1])
        print('No') if dp[n % 2][n] / n > 0.5 else print("Yes")
    except:
        break
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值