动态规划算法 openjudge 百练 python

https://www.icourse163.org/learn/PKU-1001894005?tid=1450413466#/learn/content
程序设计与算法(二)算法基础

1163:The Triangle

# 1163:The Triangle
# http://bailian.openjudge.cn/practice/1163
n = int(input())
D = [[-1] * n for i in range(n)]
for i in range(0, n):
    line = input().split()
    j = 0
    for l in line:
        D[i][j] = int(l)
        j += 1

maxSum = D[n - 1]  # maxSum 指向第n行
for i in range(n - 2, -1, -1):
    for j in range(0, i + 1):
        maxSum[j] = max(maxSum[j], maxSum[j + 1]) + D[i][j]
print(maxSum[0])

2533:Longest Ordered Subsequence 最长上升子序列

# 2533:Longest Ordered Subsequence
# http://bailian.openjudge.cn/practice/2533

# 第一行是序列的长度N(1≤N≤1000)。
N = int(input())
# 第二行给出序列中的N个整数,这些整数的取值范围都是0~10000。
b = input().split()
for i in range(len(b)):
    b[i] = int(b[i])

# MaxLen(k) = Max{MaxLen(i):1<i<k且ai<ak,且k≠1} + 1
# MaxLen(k) = 在ak左边、“终点”数值小于ak且长度最大的那个上升子序列的长度 + 1
# 因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列
maxLen = [1] * N  # 以第k个数为终点的 最长上升子序列的长度 maxLen[k]
for k in range(1, N):
    tmp = 0  # 第k个数左边的 上升子序列的最大长度
    for i in range(0, k):  # 查看以第i个数为终点的 最长上升子序列
        if b[i] < b[k]:  # 如果 b[k]左边的b[i] < b[k],那么b[i]才可能和b[k]构成上升子序列
            if tmp < maxLen[i]:  # 如果 maxLen[i] > 当前记录的 tmp
                tmp = maxLen[i]
    maxLen[k] = tmp + 1
print(max(maxLen))

1458:Common Subsequence 最长公共子序列

# 1458:Common Subsequence
# http://bailian.openjudge.cn/practice/1458


def longestCommonSubsequence(str1, str2):
    len1 = len(str1)
    len2 = len(str2)
    maxLen = [[0] * (len2+1) for _ in range(len1+1)]
    for i in range(1, len1+1):
        for j in range(1, len2+1):
            if str1[i-1] == str2[j-1]:
                maxLen[i][j] = maxLen[i - 1][j - 1] + 1
            else:
                maxLen[i][j] = max(maxLen[i - 1][j], maxLen[i][j - 1])
    return maxLen[len1][len2]

import sys
while True:
    line = sys.stdin.readline()
    if not line:
        break
    str1, str2 = line.split()
    print(longestCommonSubsequence(str1, str2))

主函数这样写也行

while True:
    try:
        line = input().split()
    except EOFError:
        break
    str1 = line[0]
    str2 = line[1]
    print(longestCommonSubsequence(str1, str2))

2945:拦截导弹 求最长不上升子序列的长度

# 2945:拦截导弹 求最长不上升子序列的长度
# http://bailian.openjudge.cn/practice/2945/

# 第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
k = int(input())
# 第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。
a = input().split()
for i in range(k):
    a[i] = int(a[i])
# lenlen[i]是 以a[i]为终点的 最长不上升子序列 的长度
# 子序列中的元素不一定在原序列中是连续的
lenlen = [1 for _ in range(k)]
for i in range(1, k):  # [1,k-1]
    for j in range(0, i):  # [0,i-1]
        if a[j] >= a[i]:  # 如果 a[i] 前面的 a[j] >= a[i]
            lenlen[i] = max(lenlen[i], lenlen[j] + 1)
print(max(lenlen))

1088:滑雪

# 1088:滑雪
# http://bailian.openjudge.cn/practice/1088
class Point:
    def __init__(self, h, r, c):
        self.h = h
        self.r = r
        self.c = c


def huaxue_renrenweiwo(point, field, L, R, C):  # 人人为我
    for i in range(1, R * C):  # 每次循环求points[i]的L[r][c]
        r = point[i].r
        c = point[i].c
        # L[r][c] = [r][c]周围四个点中(高度 < field[r][c] 且 L值最大的)那个点P的值 + 1
        if (r > 0 and field[r][c] > field[r - 1][c]):  # 如果上面的[r - 1][c]存在
            L[r][c] = max(L[r][c], L[r - 1][c] + 1)
        if (c > 0 and field[r][c] > field[r][c - 1]):  # 如果左边的[r][c - 1]存在
            L[r][c] = max(L[r][c], L[r][c - 1] + 1)
        if (r < R - 1 and field[r][c] > field[r + 1][c]):  # 如果下面的[r + 1][c]存在
            L[r][c] = max(L[r][c], L[r + 1][c] + 1)
        if (c < C - 1 and field[r][c] > field[r][c + 1]):  # 如果右边的[r][c + 1]存在
            L[r][c] = max(L[r][c], L[r][c + 1] + 1)
    return max(max(row) for row in L)


def huaxue_woweirenren(point, field, L, R, C):  # 我为人人
    for i in range(0, R * C):  # 每次循环求points[i]的L[r][c]
        r = point[i].r
        c = point[i].c
        # 每次循环开始时,points[i]的L值是已经最终求出了的
        # 若L[r][c]已计算出最终结果,则对于[r][c]周围比它高的点P,P的L值至少可以是L[r][c] +1。
        # 若当前记下的P的L值 < L[r][c] +1,则应将其更新为L[r][c] +1。
        if (r > 0 and field[r - 1][c] > field[r][c]):  # 如果上面的[r - 1][c]存在
            L[r - 1][c] = max(L[r - 1][c], L[r][c] + 1)
        if (c > 0 and field[r][c - 1] > field[r][c]):  # 如果左边的[r][c - 1]存在
            L[r][c - 1] = max(L[r][c - 1], L[r][c] + 1)
        if (r < R - 1 and field[r + 1][c] > field[r][c]):  # 如果下面的[r + 1][c]存在
            L[r + 1][c] = max(L[r + 1][c], L[r][c] + 1)
        if (c < C - 1 and field[r][c + 1] > field[r][c]):  # 如果右边的[r][c + 1]存在
            L[r][c + 1] = max(L[r][c + 1], L[r][c] + 1)
    return max(max(row) for row in L)


RC = input().split()
R = int(RC[0])
C = int(RC[1])
point = [0] * C * R  # 用一维数组存储所有的点,以便排序处理
field = [[0] * C for _ in range(R)]  # 高度矩阵
for i in range(0, R):
    RC = input().split()
    for j in range(0, C):
        field[i][j] = int(RC[j])
        point[i * C + j] = Point(int(RC[j]), i, j)
L = [[1] * C for _ in range(R)]  # 从点(i,j)出发的最长滑行长度

import operator

point.sort(key=operator.attrgetter('h'))

# print(huaxue_renrenweiwo(point, field, L, R, C))
print(huaxue_woweirenren(point, field, L, R, C))

2755:神奇的口袋

# 2755:神奇的口袋
# http://bailian.openjudge.cn/practice/2755


def Ways_recursion(volume, k):  # 从前k种物品中选择一些,凑成体积volume的做法数目 # 递归
    if (volume == 0):  # 如果体积 == 0,那么可以凑成体积volume
        return 1
    if (k <= 0):  # 如果物品 == 0,那么不可以凑成体积volume
        return 0
    # 从前(k - 1)种物品中选择一些,凑成体积volume的做法数目 +
    # 从前(k - 1)种物品中选择一些,凑成体积(volume - a[k])的做法数目
    return Ways(volume, k - 1) + Ways(volume - a[k], k - 1)


def Ways_Dynamic_Programming(Total_volume, N, a):  # 动态规划
    # Ways[volume][k]表示从前k种物品里凑出体积volume的方法数
    Ways = [[0] * (N + 1) for _ in range(Total_volume + 1)]
    # 凑出体积0的方法数 = 1
    Ways[0] = [1] * (N + 1)

    for volume in range(1, Total_volume + 1):  # 遍历体积[1,Total_volume]
        for k in range(1, N + 1):  # 遍历物品
            Ways[volume][k] = Ways[volume][k - 1]  # 从前k种物品里凑出体积volume的方法数
            if (volume >= a[k]):  # 如果剩余的体积 >= 第k个物品的体积
                # 那么再 + 从前(k - 1)种物品里凑出体积(volume - a[k])的方法数
                Ways[volume][k] += Ways[volume - a[k]][k - 1]
    # 从前N种物品中选择一些,凑成体积Total_volume的做法数目
    return Ways[Total_volume][N]


Total_volume = 40
# 输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。
N = int(input())
# 接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。
a = [0] * (N + 1)
for i in range(1, N + 1):
    a[i] = int(input())

# 从前N种物品中选择一些,凑成体积Total_volume的做法数目
# print(Ways_recursion(Total_volume, N))
print(Ways_Dynamic_Programming(Total_volume, N, a))

4131:Charm Bracelet 超时

# 4131:Charm Bracelet
# http://bailian.openjudge.cn/practice/4131
# 使用滚动数组的人人为我的动态规划程序


class Item:
    def __init__(self, w, d):
        self.w = w
        self.d = d


NM = input().split()
num_item = int(NM[0])  # N种物品
Total_volume = int(NM[1])  # 容积为M
# 第二行到第N+1行:每行两个整数W和D,描述一个物品的体积和价值。
items = [0] * (num_item + 1)
for i in range(1, num_item + 1):
    WD = input().split()
    items[i] = Item(int(WD[0]), int(WD[1]))

# F(i,j)表示在前i种物品中取若干种,在其总体积不超过j的条件下能获得的最大价值
# 对f数组进行初始化,初始化结束后,f[j]的值就代表在前一种物品中选取,总体积不能超过j,
# 此时所能获得的最大价值即前述F(1,j)。
f = [0] * (Total_volume + 1)
for j in range(0, Total_volume + 1):  # 在前i=1种物品中取若干种
    if items[1].w <= j:  # 若第1种物品的体积 <= j,则必取之,F(1,j)值为D[1]
        f[j] = items[1].d
    else:
        f[j] = 0  # 若第1种物品的体积 > j,则不能取,F(1,j)值为0
for i in range(2, num_item + 1):  # i代表从前i种物品中选取
    for j in range(Total_volume, -1, -1):  # j表示总体积不能超过j,从右到左计算f的值
        if items[i].w <= j:
            # f[i][j] = max(f[i - 1][j], f[i - 1][j - items[i].w] + items[i].d) # 矩阵
            f[j] = max(f[j], f[j - items[i].w] + items[i].d)  # 滚动数组
# 若取了第i种物品,则剩下要做的事情就是F(i-1,j- W[i])
# 求从前i-1种物品中选取若干种,在其总体积不超过j一W[i]的条件下所能获得的最大价值
# 第一类取法所能获得的最大价值是F(i-1,j-W[i]) +D[i]
# 若不取第i种物品,则剩下的问题就变成F(i-1,j)
# 第二类取法所能获得的最大价值是F(i-1,j)

print(f[Total_volume])  # f[Total_volume]的值就是F(num_item,Total_volume)

2192:Zipper

# 2192:Zipper
# http://bailian.openjudge.cn/practice/2192/
'''
给定三个字符串,判断是否可以将前两个字符串中的字符组合成第三个字符串。 
前两个字符串可以任意混合,但每个字符串必须保持其原始顺序。
'''

def zipperstr1(str1, str2, str3):
    dp = [[0 for _ in range(len(str2) + 1)] for _ in range(len(str1) + 1)]
    dp[0][0] = 1
    for i in range(0, len(str1) + 1):  # [0,len(str1)]
        for j in range(0, len(str2) + 1):  # [0,len(str2)]
            # str1的[0,i-1-1]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j-1-1]字符 匹配 and
            # str1的[i-1]字符 能和 str3的[i+j-1]字符 匹配,那么
            # str1的[0,i-1]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j-1]字符 匹配
            if (i >= 1 and dp[i - 1][j] == 1 and str1[i - 1] == str3[i + j - 1]):
                dp[i][j] = 1
            # str1的[0,i-1]字符 和 str2的[0,j-1-1]字符 能和 str3的[0,i+j-1-1]字符 匹配 and
            # str2的[j-1]字符 能和 str3的[i+j-1]字符 匹配,那么
            # str1的[0,i-1]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j-1]字符 匹配
            if (j >= 1 and dp[i][j - 1] == 1 and str2[j - 1] == str3[i + j - 1]):
                dp[i][j] = 1
    return dp[len(str1)][len(str2)]


def zipperstr2(str1, str2, str3):
    dp = [[0 for _ in range(len(str2) + 1)] for _ in range(len(str1) + 1)]
    dp[0][0] = 1
    for i in range(0, len(str1) + 1):  # [0,len(str1)]
        for j in range(0, len(str2) + 1):  # [0,len(str2)]
            # str1的[0,i-1]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j-1]字符 匹配 and
            # str1的[i]字符 能和 str3的[i+j]字符 匹配,那么
            # str1的[0,i]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j]字符 匹配
            if (i < len(str1) and str1[i] == str3[i + j] and dp[i][j]):
                dp[i + 1][j] = 1
            # str1的[0,i-1]字符 和 str2的[0,j-1]字符 能和 str3的[0,i+j-1]字符 匹配 and
            # str2的[j]字符 能和 str3的[i+j]字符 匹配,那么
            # str1的[0,i-1]字符 和 str2的[0,j]字符 能和 str3的[0,i+j]字符 匹配
            if (j < len(str2) and str2[j] == str3[i + j] and dp[i][j]):
                dp[i][j + 1] = 1
    return dp[len(str1)][len(str2)]


# 输入的第一行包含一个从 1 到 1000 的正整数。
# 它表示要跟随的数据集的数量。 每个数据集的处理都是相同的。
# 数据集出现在以下几行中,每行一个数据集。
num_dataset = int(input())
i_dataset = 1
while i_dataset <= num_dataset:
    # 对于每个数据集,输入行由三个字符串组成,由一个空格分隔。
    # 所有字符串仅由大写和小写字母组成。
    # 第三个字符串的长度总是前两个字符串的长度之和。
    # 前两个字符串的长度将在 1 到 200 个字符之间,包括 1 到 200 个字符。
    line = input().split()
    str1 = line[0]
    str2 = line[1]
    str3 = line[2]
    if zipperstr1(str1, str2, str3): 
        print("Data set %d: yes" % (i_dataset))
    else:
        print("Data set %d: no" % (i_dataset))
    i_dataset += 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzz的学习笔记本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值