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

目录

问题 A: 最长递增子序列

问题 B: 构造最长递增子序列

问题 C: 出列人数

问题 D: 矩阵连乘问题-备忘录法求最优值

问题 E: 矩阵连乘问题-动态规划求最优值

问题 F: 补充能量

问题 G: 道具的魅力值


问题 A: 最长递增子序列

题目描述

给出一个序列a1,a2,a3,a4,a5,a6,a7...an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质:s1<s2<s3<...<sn并且这个子序列的长度最长。输出这个最长子序列的长度,要求时间复杂度为O(n2)。

输入

每组输入包括两行,第一行为序列长度n,第二行为序列。

输出

输出最长递增子序列的长度。

思路:dp[i]表示以第i个数为结尾的序列长度,那么dp[i] = max(dp[j] + 1),j=0,1,2,,,n。

while True:
    try:
        n, strings = int(input()), [int(i) for i in input().split()]
        dp = [1] * n
        for i in range(1, n):
            for j in range(i):
                if strings[i] > strings[j] and dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
        # print(dp)
        print(max(dp))
    except:
        break

问题 B: 构造最长递增子序列

题目描述

在“最长递增子序列”的基础上对代码进行改进,输出一条最长递增子序列。

输入

每组输入包括两行,第一行为序列长度n,第二行为序列。
输出

输出最长递增子序列中的任意一条即可。

思路:和第一题一样的dp思路,引入新列表m,m[i]=j表示第i个数的前一个数是第j个数。

while True:
    try:
        n, strings = int(input()), [int(i) for i in input().split()]
        dp, m = [1] * n, [i for i in range(n)]
        for i in range(1, n):
            for j in range(i):
                if strings[i] > strings[j] and dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
                    m[i] = j
        # print(dp, m)
        index, res = dp.index(max(dp)), ""
        while index != -1:
            # print(index, dp[index])
            res = f"{strings[index]} " + res
            index = m[index] if m[index] != index else -1
        print(res.strip())
    except:
        break

问题 C: 出列人数

题目描述

有N位同学站在一排,体育老师要请其中的(N-K)位同学出列,将剩下的K位同学从左到右依次编号为1,2,3,…K,他们的身高分别为T1,T2,T3,…TK,要求满足T1<T2<T3<…<TK。已知N位同学的身高,请设计一个算法,计算最少需要几位同学出列可使得剩下的同学满足上述要求。

输入

多组输入,对于每一组测试数据,第1行N表示同学数量(n<=1000)。
第2行包含N个正整数,分别表示每一个同学的身高。(单位:厘米)

输出

输出最少需要出列的同学人数。

思路:第一题的小变种,从题意不难看出,答案就是长度n减去最长递增子序列的长度。

while True:
    try:
        n, strings = int(input()), [int(i) for i in input().split()]
        dp = [1] * n
        for i in range(1, n):
            for j in range(i):
                if strings[i] > strings[j] and dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
        print(n - max(dp))
    except:
        break

问题 D: 矩阵连乘问题-备忘录法求最优值

题目描述

使用备忘录法求解矩阵连乘问题,输出最少乘法次数。

输入

每组数据包括两行,第一行为数组长度n,第二行为存储矩阵维数的一维数组。

输出

矩阵连乘最优计算次数。

思路:稍微注意一下p数组的含义,如样例输入,虽然输入为p数组的长度为7,但是连乘矩阵只有6个。

def check_up_load(x: int, y: int):
    if dp[x][y] > 0:
        return dp[x][y]
    if x == y:
        return 0
    res = check_up_load(x + 1, y) + p[x - 1] * p[x] * p[y]
    for j in range(x + 1, y):
        t = check_up_load(x, j) + check_up_load(j + 1, y) + p[x - 1] * p[j] * p[y]
        res = min(res, t)
    dp[x][y] = res
    return res


while True:
    try:
        n, p = int(input()), [int(i) for i in input().split()]
        dp = [[0] * n for _ in range(n)]
        print(check_up_load(1, n - 1))
    except:
        break

问题 E: 矩阵连乘问题-动态规划求最优值

题目描述

使用动态规划算法求解矩阵连乘问题,输出最少乘法次数。

输入

每组数据包括两行,第一行为数组长度n,第二行为存储矩阵维数的一维数组。

输出

矩阵连乘最优计算次数。

思路:和前一题没有太大的差异,但是因为比较特殊,稍微注意一下dp的顺序。

def check_up_load(x: int, y: int):
    for r in range(2, y + 1):
        for i in range(1, y - r + 2):
            j = i + r - 1
            dp[i][j] = dp[i + 1][j] + p[i - 1] * p[i] * p[j]
            for k in range(i + 1, j):
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j])
    return dp[x][y]


while True:
    try:
        n, p = int(input()), [int(i) for i in input().split()]
        dp = [[0] * n for _ in range(n)]
        print(check_up_load(1, n - 1))
    except:
        break

问题 F: 补充能量

题目描述

一年一度的宇宙超级运动会在宇宙奥特英雄体育场隆重举行。X星人为这场运动会准备了很长时间,他大显身手的时刻终于到了!
为了保持良好的竞技状态和充沛的体能,X星人准备了N个不同的能量包,每个能量包都有一个重量值和能量值。由于这些能量包的特殊性,必须要完整地使用一个能量包才能够发挥功效,否则将失去能量值。
考虑到竞赛的公平性,竞赛组委会规定每个人赛前补充的能量包的总重量不能超过W。
现在需要你编写一个程序计算出X星人能够拥有的最大能量值是多少?

输入

单组输入。
第1行包含两个正整数N和W,其中N<=103,W<=103。
第2行包含N个正整数,分别表示每一个能量包的重量,两两之间用空格隔开。
第3行包含N个正整数,分别表示每一个能量包的能量值,两两之间用空格隔开。

输出

输出X星人能够拥有的最大能量值。

思路:经典的0/1背包问题

二维数组:

n, w = map(int, input().split())
v, c, dp = [int(i) for i in input().split()], [int(i) for i in input().split()], [[0] * (w + 1) for _ in range(n + 1)]
# print(dp)
for i in range(1, n + 1):
    for j in range(w + 1):
        # print(i, j)
        if v[i - 1] > j:
            dp[i][j] = dp[i - 1][j]
        else:
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i - 1]] + c[i - 1])
# print(dp)
print(dp[n][w])

滚动数组:

n, w = map(int, input().split())
v, c, dp = [int(i) for i in input().split()], [int(i) for i in input().split()], [0] * (w + 1)
# print(dp)
for i in range(1, n + 1):
    for j in range(w, v[i - 1] - 1, -1):
        dp[j] = max(dp[j], dp[j - v[i - 1]] + c[i - 1])
# print(dp)
print(dp[w])

问题 G: 道具的魅力值

题目描述

在某网络游戏中提供了一个道具库,在道具库中每种道具均有若干件(数量已知),游戏玩家购买一件道具将获得一定的魅力值。
已知每种道具的价格和魅力值,请编写一个程序,在总价格不超过某个上限的情况下使得所购道具的魅力值之和达到最大。

输入

每组测试数据的输入有n+1行,n表示道具的种类。(n<=100,p<=10000)
第1行包含两个正整数,分别表示道具种类数n和总价值的上限p,两个数字之间用空格隔开。
第2行到第n+1行分别对应于第1种道具到第n种道具的信息,每1行包含三个正整数,两个数字之间用空格隔开,三个正整数分别表示某一种道具的数量、单个道具的价格和魅力值。

输出

每组测试数据的输出只有一行,即道具魅力值的最大和。

思路:可以直接dp,也可以使用贪心法,这题贪心法比较快。

dp:

while True:
    try:
        n, p = map(int, input().split())
        nums, prices, values, dp = [], [], [], [0] * (p + 1)
        for i in range(n):
            x, y, z = map(int, input().split())
            nums.append(x), prices.append(y), values.append(z)
        for index, price in enumerate(prices):
            for j in range(p, price - 1, -1):
                for k in range(min(nums[index], j // price) + 1):
                    dp[j] = max(dp[j], dp[j - k * price] + k * values[index])
        print(dp[p])
    except:
        break

贪心法:

while True:
    try:
        n, p = map(int, input().split())
        boxs, res = [tuple(map(int, input().split())) for _ in range(n)], 0
        boxs.sort(key=lambda x: x[2]/x[1], reverse=True)
        # print(boxs)
        for i in range(n):
            if p >= boxs[i][0] * boxs[i][1]:
                res += boxs[i][0] * boxs[i][2]
                p -= boxs[i][0] * boxs[i][1]
            else:
                res += (p // boxs[i][1]) * boxs[i][2]
                p -= (p // boxs[i][1]) * boxs[i][1]
        print(res)
    except:
        break
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值