蓝桥杯真题——地宫取宝(动态规划)python

题目描述

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

输入

输入一行3个整数,用空格分开:n m k (1< =n,m< =50, 1< =k< =12)
接下来有 n 行数据,每行有 m 个整数 Ci (0< =Ci< =12)代表这个格子上的宝物的价值

输出

要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

思路:
采用动态规划策略,用 X [ i ] [ j ] X[i][j] X[i][j]表示第i行第j列的宝物的价值。 d p [ i ] [ j ] [ x ] [ y ] dp[i][j][x][y] dp[i][j][x][y]表示小明走到了 [ i ] [ j ] [i][j] [i][j]取了 x x x件物品且最大价值小于 y y y的策略数。
考虑递推关系,只能从 [ i − 1 ] [ j ] 和 [ i ] [ j − 1 ] [i-1][j]和[i][j-1] [i1][j][i][j1]走到 [ i ] [ j ] [i][j] [i][j],小明可以不取 X [ i ] [ j ] X[i][j] X[i][j]也可以取 X [ i ] [ j ] X[i][j] X[i][j],得 d p [ i ] [ j ] [ x ] [ y ] dp[i][j][x][y] dp[i][j][x][y]状态转移方程如下所示:
  当 y < = X [ i ] [ j ] y<=X[i][j] y<=X[i][j]时,不能取 X [ i ] [ j ] X[i][j] X[i][j](由dp的定义可得)则 d p [ i ] [ j ] [ x ] [ y ] = d p [ i − 1 ] [ j ] [ x ] [ y ] + d p [ i ] [ j − 1 ] [ x ] [ y ] dp[i][j][x][y]=dp[i-1][j][x][y]+dp[i][j-1][x][y] dp[i][j][x][y]=dp[i1][j][x][y]+dp[i][j1][x][y]
  当 y > X [ i ] [ j ] y>X[i][j] y>X[i][j]时,此时可以取也可以不取,那么有四部分组成: n u m 1 = d p [ i − 1 ] [ j ] [ x ] [ y ] + d p [ i ] [ j − 1 ] [ x ] [ y ] ]       ( 不 取 X [ i ] [ j ] ) n u m 2 = d p [ i − 1 ] [ j ] [ x − 1 ] [ X [ i ] [ j ] ] + d p [ i ] [ j − 1 ] [ x − 1 ] [ X [ i ] [ j ] ]       ( 取 X [ i ] [ j ] ) d p [ i ] [ j ] [ x ] [ y ] = n u m 1 + n u m 2 num1=dp[i-1][j][x][y]+dp[i][j-1][x][y]] \ \ \ \ \ (不取X[i][j])\\num2=dp[i-1][j][x-1][X[i][j]]+dp[i][j-1][x-1][X[i][j]]\ \ \ \ \ (取X[i][j])\\ dp[i][j][x][y]=num1+num2 num1=dp[i1][j][x][y]+dp[i][j1][x][y]]     (X[i][j])num2=dp[i1][j][x1][X[i][j]]+dp[i][j1][x1][X[i][j]]     (X[i][j]dp[i][j][x][y]=num1+num2
初始条件,对于 d p [ 0 ] [ 0 ] [ x ] [ y ] dp[0][0][x][y] dp[0][0][x][y] 当x==0时,应当为1(取0个只有一种方案); x = 1 & y > X [ 0 ] [ 0 ] x=1 \& y > X[0][0] x=1&y>X[0][0]时应当为1。其余情况均为0.
Python代码如下(相同的代码,C++4ms跑完,Python121ms,只能说确实不适合用来刷算法题很容易就超时了)

n,m,k = map(int,input().strip().split())
X = [[0]+list(map(int,input().strip().split()))for i in range(0, n)]
X.insert(0,[0 for i in range(0,m+1)])
mod = 1000000007

# dp[i][j][k][v] 表示从00走到ij 取了K件物品  最大价值小于等于v的方案数
dp = [[[[0 for x in range(0, 14)]for t in range(0, k+1)]for j in range(0, m+1)]for i in range(0, n+1)]

for i in range(1,n+1):
    for j in range(1,m+1):
        # 取了x个
        for x in range(0,k+1):
            # 最大价值为y
            for y in range(0,14):
                if i==1 and j ==1:
                    if x==0 or (x==1 and y > X[i][j]):
                        dp[i][j][x][y]=1
                else:
                    temp = 0
                    if k!=0 and y > X[i][j]:
                        temp = temp+dp[i-1][j][x-1][X[i][j]]+dp[i][j-1][x-1][X[i][j]]
                    temp = temp+dp[i-1][j][x][y]+dp[i][j-1][x][y]
                    dp[i][j][x][y] = temp%mod

print(dp[n][m][k][13])
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值