将数字列表分成2个相等的总和列表的算法

Lak*_*sad 23 python algorithm np-complete knapsack-problem dynamic-programming

有一个数字列表.

该列表将分为2个相等大小的列表,总和之间的差异最小.必须打印总和.

#Example:
>>>que = [2,3,10,5,8,9,7,3,5,2]
>>>make_teams(que)
27 27
Run Code Online (Sandbox Code Playgroud)

在某些情况下,以下代码算法是否存在错误?

我如何优化和/或pythonize这个?

def make_teams(que):
    que.sort()
    if len(que)%2: que.insert(0,0)
    t1,t2 = [],[]
    while que:
    val = (que.pop(), que.pop())
    if sum(t1)>sum(t2):
        t2.append(val[0])
        t1.append(val[1])
    else:
        t1.append(val[0])
        t2.append(val[1])
    print min(sum(t1),sum(t2)), max(sum(t1),sum(t2)), "\n"
Run Code Online (Sandbox Code Playgroud)

问题来自http://www.codechef.com/problems/TEAMSEL/

Geo*_*lly 29

动态编程是您正在寻找的解决方案.

[4,3,10,3,2,5]的示例:

X-Axis: Reachable sum of group.        max = sum(all numbers) / 2    (rounded up)
Y-Axis: Count elements in group.       max = count numbers / 2       (rounded up)

      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  |  | 4|  |  |  |  |  |  |  |  |  |  |       //  4
 2  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |  |  |  |  |  |       //  3
 2  |  |  |  |  |  |  | 3|  |  |  |  |  |  |  |
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |10|  |  |  |  |       // 10
 2  |  |  |  |  |  |  | 3|  |  |  |  |  |10|10|
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |10|  |  |  |  |       //  3
 2  |  |  |  |  |  | 3| 3|  |  |  |  |  |10|10|
 3  |  |  |  |  |  |  |  |  |  | 3|  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  | 2| 3| 4|  |  |  |  |  |10|  |  |  |  |       //  2
 2  |  |  |  |  | 2| 3| 3|  |  |  |  | 2|10|10|
 3  |  |  |  |  |  |  |  | 2| 2| 3|  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  | 2| 3| 4| 5|  |  |  |  |10|  |  |  |  |       //  5
 2  |  |  |  |  | 2| 3| 3| 5| 5|  |  | 2|10|10|
 3  |  |  |  |  |  |  |  | 2| 2| 3| 5| 5|  |  |
                                       ^

12是我们的幸运数字!回溯以获得该组:

12 - 5 = 7        {5}
 7 - 3 = 4        {5, 3}
 4 - 4 = 0        {5, 3, 4}

然后可以计算另一组:{4,3,10,3,2,5} - {5,3,4} = {10,3,2}

带有数字的所有字段都是一个包的可能解决方案.选择右下角最远的那个.

BTW:它被称为背包问题.

如果所有权重(w1,...,wn和W)都是非负整数,则可以使用动态编程在伪多项式时间内解决背包问题.

  • 伪多项式时间(http://en.wikipedia.org/wiki/Pseudo-polynomial_time)表示时间是输入数字大小的多项式,但仍然是输入长度的非多项式.如果输入的数字大小是有界的,那么您有一个多项式时间算法.但如果它是无限的,那么不是.例如,如果背包的n个输入数是2 ^ 0,2 ^ 1,...,2 ^(n-1),那么在动态编程解决方案的最后一步中有2 ^ n个解决方案需要检查. (11认同)

Unk*_*own 6

新解决方案

这是一种利用启发式剔除的广度优先搜索.树被限制在玩家的深度/ 2.玩家总和限额为totalscores/2.玩家池数为100,需要大约10秒才能解决.

def team(t):
    iterations = range(2, len(t)/2+1)

    totalscore = sum(t)
    halftotalscore = totalscore/2.0

    oldmoves = {}

    for p in t:
        people_left = t[:]
        people_left.remove(p)
        oldmoves[p] = people_left

    if iterations == []:
        solution = min(map(lambda i: (abs(float(i)-halftotalscore), i), oldmoves.keys()))
        return (solution[1], sum(oldmoves[solution[1]]), oldmoves[solution[1]])

    for n in iterations:
        newmoves = {}
        for total, roster in oldmoves.iteritems():
            for p in roster:
                people_left = roster[:]
                people_left.remove(p)
                newtotal = total+p
                if newtotal > halftotalscore: continue
                newmoves[newtotal] = people_left
        oldmoves = newmoves

    solution = min(map(lambda i: (abs(float(i)-halftotalscore), i), oldmoves.keys()))
    return (solution[1], sum(oldmoves[solution[1]]), oldmoves[solution[1]])

print team([90,200,100])
print team([2,3,10,5,8,9,7,3,5,2])
print team([1,1,1,1,1,1,1,1,1,9])
print team([87,100,28,67,68,41,67,1])
print team([1, 1, 50, 50, 50, 1000])

#output
#(200, 190, [90, 100])
#(27, 27, [3, 9, 7, 3, 5])
#(5, 13, [1, 1, 1, 1, 9])
#(229, 230, [28, 67, 68, 67])
#(150, 1002, [1, 1, 1000])
Run Code Online (Sandbox Code Playgroud)

另请注意,我尝试使用GS的描述解决此问题,但仅通过存储运行总计来获取足够的信息是不可能的.如果您同时存储项目数和总数,那么它将与此解决方案相同,除非您保留了不必要的数据.因为您只需要将n-1和n次迭代保持为numplayers/2.

我有一个基于二项式系数的旧的详尽的(在历史中看).它解决了长度为10的示例性问题,但后来我看到竞争对手的人数达到了100.