Dynamic Programming

本文深入探讨了四种经典的动态规划算法案例:凑硬币问题、最长非降子序列、走网格收集苹果及合唱团学生能力值乘积最大化问题。通过详细的状态定义、状态转移方程及代码实现,为读者提供了理解和解决复杂动态规划问题的实用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

凑硬币

最长非降子序列

走网格

合唱团


凑硬币

def fewest_coins(n):
    """
    面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
    :状态 : d(i) = j 表示凑够i元最少需要j个硬币
    :状态转移方程: d(i) =min{d(i-vj)+1}
    :解释: 所有之前一个硬币之差的最优解的最小值
    """
    Min = [i for i in range(n+1)]
    coins = [1,3,5]
    for i in range(1,n+1):
        for j in coins:
            if Min[i-j]+1 < Min[i] and j<=i:
                Min[i] = Min[i-j]+1

    return Min[n]

最长非降子序列

def LIS(A):
    """
    一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列的长度
    :状态:           d(i),表示前i个数中以A[i]结尾的最长非降子序列的长度
    :状态转移方程:    d(i) = max{1, d(j)+1},其中j<i,A[j]<=A[i]
    :解释:就把i前面的各个子序列中, 最后一个数不大于A[i]的序列长度加1,然后取出最大的长度即为d(i)
    """
    n = len(A)
    d = [1]*n
    max_len =1
    for i in range(n):
        for j in range(i):
            if A[j]<A[i] and d[j]+1 > d[i]:
                d[i] = d[j]+1
        if d[i]>max_len:
            max_len=d[i]

    return max_len

走网格

def grid_apple(A):
    """
    平面上有N*M个格子,每个格子中放着一定数量的苹果。你从左上角的格子开始, 每一步只能向下走或是向右走,每次走到一个格子上就把格子里的苹果收集起来, 这样下去,你最多能收集到多少个苹果。
    :状态:        S[i][j]表示我们走到(i, j)这个格子时,最多能收集到多少个苹果
    :状态转换:     S[i][j]=A[i][j] + max(S[i-1][j], if i>0 ; S[i][j-1], if j>0),A[i][j]代表格子(i, j)处的苹果数量
    :解释:到达一个格子的方式最多只有两种:从左边来的(除了第一列)和从上边来的(除了第一行)
    """
    m,n = len(A),len(A[0])
    S = [[0]*n]*m
    S[0][0] = A[0][0]

    for i in range(m):
        for j in range(n):
            if i ==0 and j>0:
                S[i][j] = A[i][j] + S[i][j - 1]
            if j ==0 and i >0:
                S[i][j] = A[i][j] + S[i-1][j]
            if i >0 and j>0:
                S[i][j] = A[i][j]+max(S[i-1][j],S[i][j-1])

    return max(max(S))

if __name__ == "__main__":
    A = [[1,1,1,1],
         [10,1,10,0],
         [1,1,0,0]]
    print(grid_apple(A))

合唱团


    有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
3
7 4 7
2 50
N = int(input())
ab = [int(x) for x in input().split()]
K,D = [int(x) for x in input().split()]

# dmax[i][j]表示前i个人中选j个人的最大值/最小值,第j个选择必须是i
dmax = [[0]*N for i in range(K)]
dmin = [[0]*N for j in range(K)]

res = (1 << 64) * -1


for i in range(N):                              # 选择第i个人
    dmax[0][i] = ab[i]
    dmin[0][i] = ab[i]
    # k=0已经初始化,直接从k=1开始
    for k in range(1,K):                        # 选择k个人
        for pre in  range(max(0,i-D),i):        # 在D约束条件下,与pre个可能的(k-1)个情况相乘选最大
            dmax[k][i] = max(dmax[k][i],max(dmax[k - 1][pre] * ab[i],dmin[k - 1][pre] * ab[i]))
            dmin[k][i] = min(dmin[k][i], min(dmax[k - 1][pre] * ab[i], dmin[k - 1][pre] * ab[i]))
    res = max(res, dmax[K - 1][i])

print(res)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值