首页 > 试题广场 >

丢棋子问题

[编程题]丢棋子问题
  • 热度指数:19512 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

一座大楼有 n+1 层,地面算作第0层,最高的一层为第 n 层。已知棋子从第0层掉落肯定不会摔碎,从第 i 层掉落可能会摔碎,也可能不会摔碎。

给定整数 n 作为楼层数,再给定整数 k 作为棋子数,返回如果想找到棋子不会摔碎的最高层数,即使在最差的情况下扔的最小次数。一次只能扔一个棋子。

数据范围: 
要求:空间复杂度 ,时间复杂度 (m是最终返回的结果)
示例1

输入

10,1

输出

10

说明

因为只有1棵棋子,所以不得不从第1层开始一直试到第10层,在最差的情况下,即第10层是不会摔坏的最高层,最少也要扔10次   
示例2

输入

3,2

输出

2

说明

先在2层扔1棵棋子,如果碎了,试第1层,如果没碎,试第3层   
示例3

输入

105,2

输出

14

说明

第一个棋子先在14层扔,碎了则用仅存的一个棋子试1~13层
若没碎,第一个棋子继续在27层扔,碎了则用仅存的一个棋子试15~26层
若没碎,第一个棋子继续在39层扔,碎了则用仅存的一个棋子试28~38层
若没碎,第一个棋子继续在50层扔,碎了则用仅存的一个棋子试40~49层
若没碎,第一个棋子继续在60层扔,碎了则用仅存的一个棋子试51~59层
若没碎,第一个棋子继续在69层扔,碎了则用仅存的一个棋子试61~68层
若没碎,第一个棋子继续在77层扔,碎了则用仅存的一个棋子试70~76层
若没碎,第一个棋子继续在84层扔,碎了则用仅存的一个棋子试78~83层
若没碎,第一个棋子继续在90层扔,碎了则用仅存的一个棋子试85~89层
若没碎,第一个棋子继续在95层扔,碎了则用仅存的一个棋子试91~94层
若没碎,第一个棋子继续在99层扔,碎了则用仅存的一个棋子试96~98层
若没碎,第一个棋子继续在102层扔,碎了则用仅存的一个棋子试100、101层
若没碎,第一个棋子继续在104层扔,碎了则用仅存的一个棋子试103层
若没碎,第一个棋子继续在105层扔,若到这一步还没碎,那么105便是结果

备注:

# -*- coding: utf-8 -*-

import math


#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
# 返回最差情况下扔棋子的最小次数
# @param n int整型 楼层数
# @param k int整型 棋子数
# @return int整型
#
class Solution1:
    """
    题目:
        https://www.nowcoder.com/practice/d1418aaa147a4cb394c3c3efc4302266?tpId=196&tqId=37178&rp=1&ru=/exam/oj&qru=/exam/oj&sourceUrl=%2Fexam%2Foj%3FjudgeStatus%3D2%26page%3D1%26pageSize%3D50%26search%3D%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196&difficulty=undefined&judgeStatus=2&tags=&title=
    算法1:
         # 超时 !!!!!

        设dp[i][j]表示i层楼,j个棋子,在最差情况下的最少试验次数
        初始化:
            dp[0][0] = 0
        状态转移方程:
            dp[i][0] = 0 # i层楼,0个棋子,无须试验
            dp[i][1] = i # i层楼,1个棋子,试验 i 次
            dp[0][j] = 0 # 0层楼,j个棋子,无须试验
            dp[1][j] = 1 # 1层楼,j个棋子,试验 1 次
            dp[i][j] = min(1 + max(dp[k - 1][j - 1], dp[i - k][j])), 1 <= k <= i,这里选取从第k层扔下一个棋子,如果碎了,那么大于k的楼层必然会碎,问题变为 k - 1 层楼,j - 1个棋子进行试验;如果没碎,那么小于k的楼层必然不碎,问题转变为i - k层楼,j个棋子进行试验。另外需要注意的是,没经过一次试验,次数+1。同时我们取得是最少试验次数。
        返回值:
            dp[n][k]
    复杂度:
        时间复杂度:O(n^2 * k)
        空间复杂度:O(n * k)
    """

    def solve(self, n, k):
        # write code here
        dp = [[10 ** 6] * (k + 1) for _ in range(n + 1)]

        for i in range(1, n + 1):
            dp[i][1] = i
        for j in range(1, k + 1):
            dp[1][j] = 1

        for i in range(2, n + 1):
            for j in range(2, k + 1):
                for l in range(1, i + 1):
                    dp[i][j] = min(dp[i][j], 1 + max(dp[l - 1][j - 1], dp[i - l][j]))

        return dp[n][k]


class Solution:
    """
    参考:
        大神:棒棒糖🍭201906101800876
    算法:
        设dp[i][j]表示i个棋子扔j次, 最多能测多少层。考虑第i个棋子在最优的楼层扔下去, 有两种情况:
            a. 碎了, 那么上面就不用测了, 下面能测的是dp[i-1][j-1]层
            b. 没碎, 那么下面就不用测了, 上面能测dp[i][j-1]层
            c. 第i个棋子扔掉的那一层也能测.
        综上,状态转移方程:
            dp[i][j] = dp[i-1][j-1] + dp[i][j-1] + 1
        显然dp[i][j]是单调递增的, 那么我们要求的答案就是在i不超过k的情况下, 使得dp[i][j] >= n的最小的j。
        不难发现,dp[i][j]只和上面元素和左上面元素有关系, 故可以压缩一维,并且需要逆向计算。在计算dp[i][j]的时候,先从右侧开始计算,算到i的时候,一开始dp[i]处为黄色,存储的是上一次迭代的结果dp[i][j-1], i-1处也为黄色,因此dp[i] = dp[i] + dp[i-1] + 1就等价于dp[i][j] = dp[i-1][j-1] + dp[i][j-1] + 1。如果反了,那么算到i的时候, i-1处就变成了粉色,转移方程就相当于dp[i][j] = dp[i-1][j] + dp[i][j-1] + 1, 导致wa
    复杂度:
        时间复杂度:O(klogn)
        空间复杂度:O(k)
    """

    def solve(self, n, k):
        # write code here
        if n == 0:
            return 0
        if k == 1:
            return n

        b = int(math.log10(n) / math.log10(2)) + 1  # 优化性质, 如果k充分大, 二分的扔即可,python27没有math.log2(x), 数学替代:log2(n) = log10(n)/log10(2)
        if k >= b:
            return b

        dp = [1] * (k + 1)  # 初始值, 不管有几个棋子, 扔1次只能测1层
        res = 1  # 最少扔1次

        while True:
            res += 1  # 每一轮迭代, j增加1次
            for i in range(k, 1, -1):
                dp[i] = dp[i] + dp[i - 1] + 1
                if dp[i] >= n:  # 当前尝试的楼层到达n,找到解
                    return res
            dp[1] = res  # dp[1][j] = j,1个棋子扔j次,测得楼层为j


if __name__ == '__main__':
    s = Solution()

    # n, k = 10, 1

    # n, k = 3, 2

    # n, k = 105, 2

    # n, k = 874520, 7

    n, k = 1, 1000000

    res = s.solve(n, k)

    print res

发表于 2022-07-06 22:03:42 回复(0)