链接: 小红取数
初看题目,并不能分析出来规律,只用用相对暴力的方法进行“试错”。
由此,我们考虑使用动态规划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[i−1][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[i−1][j]+a[i])%k]=max(dp[i−1][j]+a[i],dp[i][(dp[i−1][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])