吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1055|回复: 2
收起左侧

[Python 原创] 【入门dp】力扣鸡蛋掉落问题和转账问题的关系初探

[复制链接]
hans7 发表于 2023-7-16 12:59
本帖最后由 hans7 于 2023-7-16 13:02 编辑

题目

我昨晚偶然编出了一道水题,名为转账问题:已知银行卡里有不超过n元,你想把卡里所有钱提到某信支付,但你无法看到卡里的钱,你只能执行若干次转账i元的操作,每次看到转账成功与失败的提示。问至少需要操作多少次。ni是自然数。形式化描述:你的策略y是一段代码,初始输入为n,代码对于实际的i = 1~n元各有一个需要的猜测次数x[i],则y = max(x[i] for i in range(1, n + 1)),所求为min(y for y in y_set)

本文CSDN:https://blog.csdn.net/hans774882968/article/details/131749241

本文juejin:https://juejin.cn/post/7255967761805197349

本文52pojie:https://www.52pojie.cn/thread-1809085-1-1.html

作者:hans774882968以及hans774882968以及hans774882968

思路

尝试转i元,1 <= i <= n。如果成功,则知道卡里不超过n - i元;如果失败,则知道卡里不超过i - 1元。因此定义dp[i]为已知银行卡里有不超过i元时的最少操作次数。边界条件:dp[0~2] = 0~2。状态转移方程:dp[i] = min(max(dp[i - j], dp[j - 1]) + 1) for j in range(1, i + 1)

接下来考虑优化这个dp转移。有一个很明显的单调性:v1 < v2dp[v1] <= dp[v2]i - j单减,j - 1单增,因此取i - j == j - 1j即为最优点。

  • i为奇数,则dp下标取到(i - 1) // 2
  • i为偶数,则dp下标取到(i - 1) // 2 + 1

取到下标可统一为i // 2。因此转移方程可以简化为:dp[i] = dp[i // 2] + 1。对于n,答案居然就是n二进制数位的个数!

于是有推论:最优策略为:总是尝试转i - i // 2 = (i + 1) // 2元。

与力扣鸡蛋掉落问题的关系

鸡蛋掉落问题传送门

经群友提醒,这个问题从状态转移方程的角度来看,和鸡蛋掉落问题的k = +∞的情况类似。两个问题从题面来看不像,但它们有一个相同点:1次操作无论获得什么反馈,都能缩小问题规模。因此我们写了下面的代码,验证k = n, n+1, n+2的情况下所得的dp数组不仅相等,还等于转账问题的dp数组。

代码

import numpy as np

def main():
    N = 2001

    dp1 = [[0] * N for _ in range(N + 2)]

    def egg_drop(k, n):
        for j in range(1, n + 1):
            dp1[1][j] = j
        for i in range(2, k + 1):
            best = 1
            for j in range(1, n + 1):
                while best <= j and dp1[i][j - best] > dp1[i - 1][best - 1]:
                    best += 1
                dp1[i][j] = 1 + min(dp1[i][j - best + 1], dp1[i - 1][best - 1])

    dp2 = np.array([0] * N)

    def bin_count(n):
        for i in range(1, n + 1):
            dp2[i] = dp2[i // 2] + 1

    dp3 = np.array([N] * N)

    def n2_dp(n):
        dp3[0] = 0
        for i in range(1, n + 1):
            for j in range(1, i + 1):
                dp3[i] = min(dp3[i], max(dp3[i - j], dp3[j - 1]) + 1)

    egg_drop(N + 1, N - 1)
    bin_count(N - 1)
    n2_dp(N - 1)
    ans1 = np.array([dp1[i][i] for i in range(N)])
    ans2 = np.array([dp1[i + 1][i] for i in range(N)])
    ans3 = np.array([dp1[i + 2][i] for i in range(N)])
    assert (ans1 == dp2).all() and (ans2 == dp2).all() and (ans3 == dp2).all() and (dp2 == dp3).all()
    print('dp2 ', dp2[:101])
    print('dp3 ', dp3[:101])
    print('ans3', ans3[:101])

if __name__ == '__main__':
    main()

免费评分

参与人数 1威望 +1 吾爱币 +10 热心值 +1 收起 理由
苏紫方璇 + 1 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

hanghaidongchen 发表于 2023-7-16 17:44
本帖最后由 hanghaidongchen 于 2023-7-16 17:46 编辑

有点没看懂那个转帐问题
 楼主| hans7 发表于 2023-7-17 15:51
hanghaidongchen 发表于 2023-7-16 17:44
有点没看懂那个转帐问题

可以先去看懂鸡蛋掉落问题,力扣里好的题解很多。看懂鸡蛋掉落问题后,我这个转账问题就秒懂了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 21:50

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表