获取将一组对象划分为两个等长(或相差一个)子集的方法列表

Obtaining list of ways to divide a set of object into two equal length (or off by one) subset

问题:

示例:

IF
S = {a, b, c, d, e, f, g, h, i, j}
L = func(S)

THEN
L == [
   { {a, b, c, d, e}, {f, g, h, i, j} },
   { {a, b, c, d, f}, {e, g, h, i, j} },
   { {a, b, c, d, g}, {f, e, h, i, j} },
   ... ]

SUCH THAT
{ {a, b, c, d, e}, {f, g, h, i, j} } 
== { {f, g, h, i, j}, {a, b, c, d, e} } 
== { {e, d, a, b, c}, {g, i, j, f, h} }

目的:我需要列出将可变数量的玩家分成两个相等(或几乎相等)的团队的所有可能方法。

到目前为止我尝试了什么:我能够使用 itertools.combinations 函数在 python 中编写代码。但是,我这样做的方式似乎效率低下。我得到了一个子集的所有组合,并通过从第一个子集中减去原始集得到了第二个子集。但是,因为我需要考虑到玩家进入哪个队伍并不重要,所以我基本上必须将那个组合分成两半。

这是我的。

import itertools as it
import math

def divide(players: set[int]):
    comb = it.combinations(players, math.floor(len(players) / 2))

    rtn = []
    for team in comb:
        a = frozenset(team)
        b = frozenset(players - a)
        element = {a, b}

        if element not in rtn:
            rtn.append(element)

    return rtn

你应该尝试回溯。速度很快,使用的代码很少!

n = int(input())

# List of solutions
# For each l in L, l[i] = True if player i is in team A, False if he is in team B
L = []

# Current solution
cur = [False]*n

# In each step, we decide the team of player i,
# Given that team A has already n_team_a players
def backtracking(i, n_team_a):
    n_team_b = i - n_team_a # Number of players on team B
    left = n - i            # Number of players left to choose

    # If we can't have a balanced team even if we put all the players
    # left in the same team, abort
    if n_team_a + left + 1 < n_team_b or n_team_b + left + 1 < n_team_a:
        return

    # If we found a solution, add it to L
    if i == n:
        L.append(cur.copy())
        return

    # Try putting player i on team A
    cur[i] = True
    backtracking(i + 1, n_team_a + 1)

    # Try putting player i on team B
    cur[i] = False
    backtracking(i + 1, n_team_a)
    
# Start
backtracking(0, 0)

# Print solutions
print(*L, sep='\n')

你快到了。

从球员中删除第一个元素并记住它作为leader

像现在一样从玩家那里形成命令。

leader添加到输出前的第一个命令

简单示例(无集合)

import itertools as it
import math

def divide(players):
    first = players.pop(0)
    comb = it.combinations(players, len(players) // 2)

    rtn = []
    for team in comb:
        a = list(team)
        a.insert(0, first)
        b = [x for x in players if x not in team]
        element = (a, b)

        if element not in rtn:
            rtn.append(element)

    return rtn


print(divide([1,2,3,4,5]))

[([1, 2, 3], [4, 5]), ([1, 2, 4], [3, 5]), 
 ([1, 2, 5], [3, 4]), ([1, 3, 4], [2, 5]), 
 ([1, 3, 5], [2, 4]), ([1, 4, 5], [2, 3])]