备战蓝桥杯python组——dp

小红取数

链接: 小红取数
在这里插入图片描述
初看题目,并不能分析出来规律,只用用相对暴力的方法进行“试错”。
由此,我们考虑使用动态规划dp来简化暴搜的过程

解题思路

确定好了使用dp,我们就要来分析dp的重中之重:动态转移方程

转移方程的确定

取数要求我们这个数%k==0,我们就要考虑将dp数组与给出的数字和加和的余数相联系起来,于是我们设:

dp[i][j]: 前i个数中(包含i)余数为j的最大和

明白了dp定义,我们来讲解状态转移方程

我们先要明确一点,当前dp[i][j]的由来可以有两个大的方向:
1.不加上当前遍历到的数字ii
2.j是由 (x+a[t]) % k ==j 中的状态x转移过来的

首先,我们来考虑当前状态dp[i][j],在不加当前遍历数字的情况下是如何得来的
无外乎两种情况:
1.由前i-1个数中,余数为k的最大数得来,也就是dp[i-1][j]
2.保持现在状态不变,也就是dp[i][j]

二者间取较大的,所以我们第一个状态转移方程为:
  d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] )   . \ dp[i][j] = max(dp[i][j], dp[i-1][j])\,.  dp[i][j]=max(dp[i][j],dp[i1][j]).
我们再来回顾一下dp状态求得的思路:
1.不加上当前遍历到的数字ii
2.j是由 (x+a[t]) % k ==j 中的状态x转移过来的

这只是其中的部分情况,我们还并没有加上当前遍历的数字,如果加上当前遍历的数字,此时我们求得的并不是当前dp[i][j]的状态,而是余数为 (j+a[i])%k时的状态,此时我们的dp[i-1][j]相当于上文提及到的x,状态转移方程为:
  d p [ i ] [ ( d p [ i − 1 ] [ j ] + a [ i ] ) % k ] = m a x ( d p [ i − 1 ] [ j ] + a [ i ] , d p [ i ] [ ( d p [ i − 1 ] [ j ] + a [ i ] ) \ dp[i][(dp[i-1][j]+a[i]) \% k] = max(dp[i-1][j]+a[i],dp[i][(dp[i-1][j]+a[i])%k]) \,.  dp[i][(dp[i1][j]+a[i])%k]=max(dp[i1][j]+a[i],dp[i][(dp[i1][j]+a[i])

代码

for i in range(1, n+1):
    for j in range(k):
        # 不加位置i的数字
        dp[i][j]=max(dp[i-1][j],dp[i][j])
        # 加位置i的数字
        dp[i][(dp[i-1][j]+a[i])%k]=max(dp[i-1][j]+a[i],dp[i][(dp[i-1][j]+a[i])%k])

整体代码

n, k = map(int, input().split())
a=[0]
a += [int(i) for i in input().split()]
dp = [[0]* k for _ in range(n+1)]
for i in range(1, n+1):
    for j in range(k):
        # 不加位置i的数字
        dp[i][j]=max(dp[i-1][j],dp[i][j])
        # 加位置i的数字
        dp[i][(dp[i-1][j]+a[i])%k]=max(dp[i-1][j]+a[i],dp[i][(dp[i-1][j]+a[i])%k])

if dp[n][0]==0:
    print(-1)
else:
    print(dp[n][0])

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值