动态规划


2023.11.28
好像突然对动态规划有点感觉了,就把坑先开了
所谓状态转移方程,可能就是后一轮的状态是要根据前一轮或几轮做出的不同抉择分支有关的
是在这个题开窍的,回头遇到更复杂的再回来填坑
https://fcqian.blog.csdn.net/article/details/127417904

2024.01.12
最近通过各种各样的机缘巧合,又刷到了几道动态规划的题,就想着回来填一下坑。
1.先看一下上面这道入坑题
给定每轮的牌面分数,可选择拿或不拿,求n轮过后可得的最高分数
若拿,则需要加上当前的牌面分;若不拿,则分数回滚至三轮前的分数
由于每轮有且仅有这两种选择,所以题目所期望的最终最大分数,一定是在最后一次,选中了此前两者之中,能使得新分数较大的那个决策
于是我们假定声明一个列表dp来存储每一轮结束后的最高分数
则有dp[n] = max(dp[n-1] + arr[n], dp[n-3])
(所谓动态规划,指的大概就是后面的轮次的结果会依赖于前面的轮次的决策)
(这个原来在运筹学上是怎么定义的我已经完全记不得了,反正暂且这么理解)
而上面得到的这个规律,就是动态规划中听起来很厉害的被叫做“状态转移方程”的解题关键
在拿到状态转移方程的基础上,细化逻辑
比如既然计算dp[n]需要知道dp[n-1]与dp[n-3]的值,那么自然就要求n大于3,起码n不能小于3
而刚好题干又声明:当轮次n小于等于3时,置总分为0
于是有dp[n] = max(dp[n-1] + arr[n], 0)   (n <= 3)
这里又涉及到n-1,所以自然要求n>=1
再向下,n值就只剩0了,最开始的轮次的最优决策显而易见,若是不拿,则保持分数为0
(或者是把分数原有的总分0再次重置为0,总之是等效的)
也就是说,当第0轮的牌面分数为正,则拿,反之不拿
即dp[0] = max(arr[0], 0)
想通这些,就可以着手编码了,程序事先声明长度为n的dp列表,设每一项的初值为0
循环 i 值从0渐增至n-1,对每一个 i 根据其在不同的区间做不同的计算
循环完毕后,列表dp[n-1]即为题解

2.第二道题的链接如下
华为OD机试 - 猴子爬山(Java & JS & Python)_js猴子爬山-CSDN博客
原博主伏城大佬说这个可以用分治递归自顶向下解,也可以用动态规划自底向上
这里先写以动态规划为主
猴哥要爬n阶,但每次只爬1或者3级,问有多少种方式
试着找一下状态转移方程:
猴哥到达n阶的时候只有两个来处,要么是从n-1级来的,要么是从n-3来的
(不用考虑n-2 因为n-2需要劳驾猴哥再向上爬一级 也就归并到n-1的结果内)
于是我们声明dp[n]表示猴哥爬到n阶的方式和,有
dp[n] = dp[n-1] + dp[n-3]
这里直接加和是因为猴哥只有一种且相同的身法
比如我开个脑洞给这个题加一个设定,说猴哥跳1级的时候比较老实,正常跳;跳3级的时候,随他心情,可能正常跳上去,也可能后空翻上去。采用不同的身法也认作不同的方式
那么dp[n] = dp[n-1] + dp[n-3] * 2
这段是我自己的理解,应该没错吧,要是我理解不对的话也欢迎各位指正

然后再接着考虑,要知道dp[n],就得知道dp[n-1]与dp[n-3],那当n逐渐减小至不足3的时候,就需要特殊处理了。
dp[1] = 1 猴哥到达第一阶只有一种情况,一种方式。就是一步上去 ( 1 )
dp[2] = 1 猴哥到达第二阶只有一种情况,一种方式。从第一阶再爬一级  (1, 1)
dp[3] = 2 猴哥到达第三阶有两种情况,两种方式。
要么从第二阶爬一级上去(1,1,1),要么从开始直接爬三级上去(3)
dp[4] = dp[3] + dp[1] = 3 到达第四阶有两种情况,共三种方式 即[ (1, 1, 1, 1) ( 3, 1) (1, 3) ]
dp[4]以后就都可以用状态转移方程了
所以程序声明长度为n+1的列表(保证索引包含n),然后对dp[1], dp[2], dp[3]赋值
再将i从4渐增至n,依次调用dp方程计算。最后的dp[n]就是题解。

第三道题在力扣,链接不太好用,放个题号
LCP 07.传递信息

小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:

  1. 有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
  2. 每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
  3. 每轮信息必须需要传递给另一个人,且信息可重复经过同一个人

给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。

示例 1:

输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3

输出:3

解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。

示例 2:

输入:n = 3, relation = [[0,2],[2,1]], k = 2

输出:0

解释:信息不能从小 A 处经过 2 轮传递到编号 2

限制:

  • 2 <= n <= 10
  • 1 <= k <= 5
  • 1 <= relation.length <= 90, 且 relation[i].length == 2
  • 0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]

这个题可以用深度优先搜索和广度优先搜索来解,同样先按下不表
先看动态规划
题目想求的是,从编号为0的开始传递信息,经K轮,到编号为n-1处停止,有多少种传递方式
首先,由于信息不是任意两者都能传的,那么最后一轮,一定是能把消息传给n-1位的人传的
此外又有轮次限制,所以就要求在k-1轮(倒数第二轮)结束时,消息刚好传到了n-1的信息源
于是我们设dp[k][n-1]来表示:消息从0开始,经k轮,传递至n-1处的方式总数
那么状态转移方程dp[k][n-1] = sum(dp[k-1][a], dp[k-1][b], dp[k-1][c].......)
其中a,b,c....都是能够向n-1传递消息的人

放一版我刚AC的代码在这里,太嫩了还是,回头有空会再对比一下官方题解优化下思路

class Solution:
    def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
        r = {}
        for f, t in relation:
            if f not in r:
                r[f] = []
            r[f].append(t)

        dp = [[0 for _ in range(n)] for _ in range(k + 1)]

        # time 1
        from_idxes = set()
        for to_idx in r[0]:
            dp[1][to_idx] = 1
            from_idxes.add(to_idx)

        for time in range(2, k + 1):
            to_idxes = set()
            for from_idx in from_idxes:
                if from_idx not in r:
                    continue
                for to_idx in r[from_idx]:
                    dp[time][to_idx] += dp[time-1][from_idx]
                    to_idxes.add(to_idx)
            from_idxes = set(to_idxes)

        return dp[k][n - 1]

还有一道题在牛客,先把坑放这
购物单_牛客题霸_牛客网
​动态规划这块,题也见了一些,总是,能读懂题,无从下手,看看题解,好像又能行了
然后到自己写的时候一练就废啪啪打脸

回头再继续补充填坑吧,今天醒来之后,加上这个周末,需要准备一下别的知识点

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值