Python 容器的最小总大小

Python 容器的最小总大小,python,algorithm,dynamic-programming,Python,Algorithm,Dynamic Programming,我正在练习一些Amazon面试编码练习,我一直在做这个练习 简而言之,给定一个列表l和一个整数d,我必须将列表分成d个部分,使每个部分的最大值之和最小,例如,如果l=[10,2,20,5,15,10,1],d=3应该返回31,因为最好的选择是[10,2,20,5,15],[10],[1]>20+10+1=31 问题的链接: 我考虑将列表拆分为2,并使用第一部分和d-1递归调用,并最大限度地利用第二部分(这第二部分将开始有一个元素,我们将在每次迭代中向这一部分添加一个元素) 我这样做了,得到了“调

我正在练习一些Amazon面试编码练习,我一直在做这个练习

简而言之,给定一个列表
l
和一个整数
d
,我必须将列表分成d个部分,使每个部分的最大值之和最小,例如,如果
l=[10,2,20,5,15,10,1],d=3
应该返回31,因为最好的选择是
[10,2,20,5,15],[10],[1]>20+10+1=31

问题的链接:

我考虑将列表拆分为2,并使用第一部分和
d-1
递归调用,并最大限度地利用第二部分(这第二部分将开始有一个元素,我们将在每次迭代中向这一部分添加一个元素)

我这样做了,得到了“调用Python对象时超过了最大递归深度”错误。我知道这个问题的解决办法很容易找到,但我想知道为什么这个方法不起作用

def getmin(l, d):
    # base case, if the list has d elements each element will be a sublist
    if len(l)==d: 
        return sum(l)
    else:
        minn = float("inf")
        for i in range(1, len(l)-d+1):
            val = getmin(l[0:-i], d-1) + max(l[-i:])
            if minn > val:
                minn = val
    return minn

对于d=1的简单情况,解决方案就是列表的最大值

def getmin(l, d):
    if d == 1:
        return max(l)
当d大于1时,解决方案是返回添加到递归调用结果的列表的头或尾中较小的一个

def getmin(l, d):
    if d == 1:
        return max(l)
    if l[0] < l[-1]:
        return l[0] + getmin(l[1:], d-1)
    else:
        return getmin(l[:-1], d-1) + l[-1]
def getmin(左、右):
如果d==1:
返回最大值(l)
如果l[0]
您的列表
l
并不像您编写的示例那样是一个2d列表,因此您的算法似乎难以保持存储您为到达特定点而进行的切割的结果列表和输入列表的直线性。保存一个单独的列表并将结果累加到其中,将
l
视为只读,这要容易得多。更不用说,从性能的角度来看,每次递归调用都复制它是痛苦的

在进一步讨论之前,Python建议。更喜欢
L
nums
lst
——很难区分
1
i
L
。我将使用
L
来完成文章的其余部分

与您类似的暴力解决方案是运行一个从0到
len(L)
的计数器,并使用递归在每个节点上尝试各种可能性。在每个
i
,我们可以将当前编号
L[i]
附加到最后一个bucket,或者以
L[i]
作为第一个元素启动一个新bucket。一个小的优化是只保留每个桶的最大值,而不是所有元素

现在,您有了一个暴力解决方案,您可以对重复的工作施加压力

from functools import cache
import random

def min_sum_of_max_elems_from_d_cuts(L, d):
    assert len(L) >= d

    @cache
    def find_best_cut(i, cuts):
        if len(cuts) == d and i == len(L):
            return sum(cuts)
        elif len(cuts) > d or i >= len(L):
            return float("inf")

        best = float("inf")

        if cuts: # try extending the current cut by L[i]
            new_cuts = cuts[:-1] + (max(cuts[-1], L[i]),)
            best = min(best, find_best_cut(i + 1, new_cuts))

        #                try making a new cut at L[i]
        return min(best, find_best_cut(i + 1, cuts + (L[i],)))

    return find_best_cut(i=0, cuts=tuple())

if __name__ == "__main__":
    tests = [
        dict(L=[10,2,20,5,15,10,1], d=3, result=31),
        dict(L=[10,2,20,5,15,10,1], d=5, result=43),
        dict(L=[5,4,2,4,3,4,5,4], d=4, result=16),
        dict(L=[22,12,1,14,17], d=2, result=39),
    ]

    for test in tests:
        assert min_sum_of_max_elems_from_d_cuts(test["L"], test["d"]) == test["result"]
    
    L = random.Random(42).choices(range(200), k=25)
    assert min_sum_of_max_elems_from_d_cuts(L, d=15) == 1056

我将把优化、自底向上的DP和循环留给读者。看起来问题与相同,因此目标约束为
1请尝试在函数顶部打印
l
d
。我很确定他们没有做你所期望的。您可能正在重载
l
作为可能的结果和输入列表。您需要一个单独的结构来获得结果。您好,谢谢您的帮助,但它不适用于案例l=[5,4,2,4,3,4,5,4],d=4,因为当最佳解决方案i 16:[5,4]、[2]、[4,3]、[4,5,4]->16时,它返回18。如果您有问题的链接,这将使人们更容易验证他们的解决方案。顺便说一句,对于这个问题,
l
d
能有多大?完成了,我添加了一个到原始问题的链接,我认为这是通过动态编程和记忆来完成的,但我不确定是否能按我计划的方式完成。谢谢。你的策略很好——可以从蛮力开始,先获得正确性,然后打个便条,或者在你有工作的时候做自底向上的DP。谢谢你的帮助,解释确实帮了大忙。我很感激。