You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kattis《邮箱制造商》问题:2D自底向上DP解法优化问询

问题分析与解答

问题背景(Kattis《邮箱制造商》)

给定k个相同邮箱(每个最多承受m个鞭炮),需找出确定邮箱最大可承受鞭炮数所需的最少鞭炮总量。规则:

  1. 若邮箱能承受x个鞭炮,则必然能承受x-1个;
  2. 邮箱爆炸则销毁,未爆炸可重复使用。

核心问题与错误分析

你提到的问题是鸡蛋掉落问题的变种,但两者核心目标不同:鸡蛋掉落求最少测试次数,本题求最少总鞭炮数。你的2D DP代码错误的核心原因是状态转移方程不符合问题逻辑:

当选择测试x个鞭炮且邮箱未爆炸时,后续需要确定的是阈值在[x, j]范围内,而非[0, j-x]。此时后续测试的鞭炮数是x+y(y为相对于x的增量),总鞭炮数并非简单的dp[i][j-x],而是与区间起点x强相关的另一个值——这也是3D DP状态dp[i][low][high]存在的意义。

3D DP递推公式中文翻译

原公式:

dp[i][low][high]=min(max(dp[i−1][low][mid−1],dp[i][mid][high])+mid)

翻译为:
dp[i][low][high]表示使用i个邮箱,确定阈值在[low, high]范围内所需的最小总鞭炮数。计算时遍历所有可能的测试鞭炮数mid,取max(炸后处理[low, mid-1]的总鞭炮数, 未炸处理[mid, high]的总鞭炮数) + mid的最小值。

2D DP实现方案

可以通过反向状态定义实现2D DP,避免3D空间开销:

状态定义

dp[i][s]:使用i个邮箱,总鞭炮数为s时,能够确定的最大阈值(即可以完全确定0~dp[i][s]范围内的所有可能阈值)。我们需要找到最小的s,使得dp[k][s] >= m。

初始条件

  • 当i=1时,只有1个邮箱,必须从1开始逐个测试,总鞭炮数s能确定的最大阈值是满足j*(j+1)/2 <= s的最大j。
  • dp[i][0] = 0:总鞭炮数为0时无法确定任何阈值。

状态转移

对于每个i(邮箱数)和s(总鞭炮数),遍历所有可能的首次测试鞭炮数x:

  • 若炸了:剩余i-1个邮箱、s-x个鞭炮,需能覆盖0~x-1的阈值(即dp[i-1][s-x] >= x-1)。
  • 若未炸:剩余i个邮箱、s-x个鞭炮,可确定的阈值范围是x~x+dp[i][s-x]。
    取所有可行x中能达到的最大阈值作为dp[i][s]。

Python代码实现

import sys
import math

def main():
    t = int(sys.stdin.readline())
    for _ in range(t):
        k, m = map(int, sys.stdin.readline().split())
        
        # 预估最大可能的总鞭炮数:1个邮箱时的情况
        max_s = m * (m + 1) // 2
        
        # 初始化dp数组:prev_dp对应i-1个邮箱,curr_dp对应i个邮箱
        prev_dp = [0] * (max_s + 1)
        # 填充i=1的情况
        for s in range(1, max_s + 1):
            # 求最大j满足j*(j+1)/2 <=s
            j = int((math.sqrt(8 * s + 1) - 1) // 2)
            prev_dp[s] = j
        
        for i in range(2, k + 1):
            curr_dp = [0] * (max_s + 1)
            for s in range(1, max_s + 1):
                max_threshold = 0
                # 遍历所有可能的首次测试鞭炮数x
                for x in range(1, s + 1):
                    remaining = s - x
                    # 炸了的情况:剩余资源需覆盖0~x-1
                    if prev_dp[remaining] >= x - 1:
                        # 未炸的情况:能达到的阈值是x + curr_dp[remaining]
                        current = x + curr_dp[remaining]
                        if current > max_threshold:
                            max_threshold = current
                curr_dp[s] = max_threshold
            prev_dp = curr_dp
        
        # 找到最小的s使得prev_dp[s] >=m
        for s in range(max_s + 1):
            if prev_dp[s] >= m:
                print(s)
                break

if __name__ == "__main__":
    main()

优化说明

  • 使用滚动数组(prev_dp和curr_dp)将空间复杂度从O(k*max_s)优化为O(max_s)。
  • 对于较大的m,可以进一步优化max_s的预估,或者使用动态扩展的数组来节省空间。

内容的提问来源于stack exchange,提问作者Karma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:23:11