从 Python 中的多个列表中仅选择一个唯一元素

Posted

技术标签:

【中文标题】从 Python 中的多个列表中仅选择一个唯一元素【英文标题】:Select only one unique element from multiple lists in Python 【发布时间】:2020-02-26 07:53:06 【问题描述】:

这不是我正在努力做的家庭作业,但我正在努力解决一个问题(如果有兴趣,请点击此处链接 https://open.kattis.com/problems/azulejos)。

在这里你实际上不必理解问题,但我现在想要完成的是,我只想从多个列表中选择一个元素,并且它们不会相互重叠。

例如,在我的代码末尾,我得到一个输出:

1: [1, 2, 3], 2: [1, 2, 3, 4], 3: [2, 4], 4: [1, 2, 3, 4]

我想将其转换为,例如,

3: 4, 2: 2, 4:1, 1: 3 -- which is the sample answer that is in the website.

但依我的理解,也可以很简单

1: 3, 2: 2, 3: 4, 4: 1

我正在努力只选择一个不与其他整数重叠的整数。我在代码中生成的字典包含具有多个整数的列表。我只想从每个中挑选一个,它们都是独一无二的

import sys

n_tiles_row = int(sys.stdin.readline().rstrip())
# print(n_tiles_row) ==> 4

# BACK ROW - JOAO
back_row_price = sys.stdin.readline().rstrip()
# print(back_row_price) ==> 3 2 1 2

back_row_height = sys.stdin.readline().rstrip()
# print(back_row_height) ==> 2 3 4 3

# FRONT ROW - MARIA
front_row_price = sys.stdin.readline().rstrip()
# print(front_row_price) ==> 2 1 2 1

front_row_height = sys.stdin.readline().rstrip()
# print(front_row_height) ==> 2 2 1 3

br_num1_price, br_num2_price, br_num3_price, br_num4_price = map(int, back_row_price.split())
# br_num1_price = 3; br_num2_price = 2; br_num3_price = 1; br_num4_price = 2;

br_num1_height, br_num2_height, br_num3_height, br_num4_height = map(int, back_row_height.split())
# 2 3 4 3

fr_num1_price, fr_num2_price, fr_num3_price, fr_num4_price = map(int, front_row_price.split())
# 2 1 2 1

fr_num1_height, fr_num2_height, fr_num3_height, fr_num4_height = map(int, front_row_height.split())
# 2 2 1 3

back_row = 1: [br_num1_price, br_num1_height], 
2: [br_num2_price, br_num2_height], 
3: [br_num3_price, br_num3_height], 
4: [br_num4_price, br_num4_height]
# 1: [3, 2], 2: [2, 3], 3: [1, 4], 4: [2, 3]

front_row = 1: [fr_num1_price, fr_num1_height], 
2: [fr_num2_price, fr_num2_height], 
3: [fr_num3_price, fr_num3_height], 
4: [fr_num4_price, fr_num4_height]
# 1: [2, 2], 2: [1, 2], 3: [2, 1], 4: [1, 3]

_dict = 1: [], 
2: [], 
3: [], 
4: []


for i in range(n_tiles_row):
    _list = []
    for n in range(n_tiles_row):
        if(list(back_row.values())[i][0] >= list(front_row.values())[n][0] 
        and list(back_row.values())[i][1] >= list(front_row.values())[n][1]):
            _list.append(list(front_row.keys())[n])
            _dict[list(back_row.keys())[i]] = _list

print(_dict)
# 1: [1, 2, 3], 2: [1, 2, 3, 4], 3: [2, 4], 4: [1, 2, 3, 4]

如果有其他方法可以解决此问题,请告诉我。

【问题讨论】:

如果您在算法的制定或实施上苦苦挣扎,我不太明白。如果只是实现,您能否制定问题中的算法?如果您对配方有疑问,您目前的想法是什么? 另外,在我看来,有多种可能的解决方案,例如1: 1, 2: 3, 3: 2, 4: 41: 2, 2: 3, 3: 4, 4: 1. @norok2 感谢您的回答。是的,有多种可能的解决方案。而且我不确定如何制定算法。当然,我可以通过检查每一个可能的组合来强制执行此操作,并且只获取唯一的项目,但是这是太多的计算,尤其是当有很多输入时 【参考方案1】:

解决问题的一种可能效率低下的方法是生成所有可能的“解决方案”(值可能不存在于与特定键对应的列表中)并解决一个“有效”的解决方案(所有值存在于相应的列表中)。

使用itertools.permutation(能够计算满足唯一性约束的所有可能解决方案)的一种方法是:

import itertools


def gen_valid(source):
    keys = source.keys()
    possible_values = set(x for k, v in source.items() for x in v)
    for values in itertools.permutations(possible_values):
        result = k: v for k, v in zip(keys, values)
        # : check that `result` is valid
        if all(v in source[k] for k, v in result.items()):
            yield result


d = 1: [1, 2, 3], 2: [1, 2, 3, 4], 3: [2, 4], 4: [1, 2, 3, 4]


next(gen_valid(d))
# 1: 1, 2: 2, 3: 4, 4: 3

list(gen_valid(d))
# [1: 1, 2: 2, 3: 4, 4: 3,
#  1: 1, 2: 3, 3: 2, 4: 4,
#  1: 1, 2: 3, 3: 4, 4: 2,
#  1: 1, 2: 4, 3: 2, 4: 3,
#  1: 2, 2: 1, 3: 4, 4: 3,
#  1: 2, 2: 3, 3: 4, 4: 1,
#  1: 3, 2: 1, 3: 2, 4: 4,
#  1: 3, 2: 1, 3: 4, 4: 2,
#  1: 3, 2: 2, 3: 4, 4: 1,
#  1: 3, 2: 4, 3: 2, 4: 1]

这会生成n! 解决方案。

在列表上使用笛卡尔积的“蛮力”方法产生prod(n_k) = n_1 * n_1 * ... * n_k 解决方案(每个列表的长度为n_k)。在最坏的情况下(最大密度),这是n ** n 解决方案,它渐近地比阶乘差得多。 在最佳情况下(最小密度),这只是 1 个解决方案。 一般来说,这可能比上面提出的“置换解决方案”更慢或更快,具体取决于列表的“稀疏性”。

平均n_k 约为。 n / 2n! 对于n >= 6 来说更小/更快。

平均 n_k 约为。 n * (3 / 4), n! 对于n >= 4 来说更小/更快。

在本例中,有4! == 4 * 3 * 2 * 1 == 24 置换解和3 * 4 * 2 * 4 == 96 笛卡尔积解。

【讨论】:

【参考方案2】:

这是一个使用与您提供的代码相同的语法的解决方案。

这里的诀窍是先按价格升序(问题要求非降序)然后按高度降序排列瓷砖,这样后排的下一个最低价格的最高瓷砖将与前排的下一个最低价格。 为了进行这种排序,我使用了 Python 的 sorted() 函数。请参阅堆栈溢出示例 here。

我假设如果没有这样的匹配,然后根据您链接的问题立即中断并打印。

作为旁注,您最初声称 Python 字典 3: 4, 2: 2, 4:1, 1: 3 等同于 1: 3, 2: 2, 3: 4, 4: 1。虽然您是对的,但您必须记住,在 Python 字典中,默认情况下对象是未排序的,因此以这种方式比较键并不容易。

import sys

n_tiles_row = int(sys.stdin.readline().rstrip())
# print(n_tiles_row) ==> 4

# BACK ROW - JOAO
back_row_price = sys.stdin.readline().rstrip()
# print(back_row_price) ==> 3 2 1 2

back_row_height = sys.stdin.readline().rstrip()
# print(back_row_height) ==> 2 3 4 3

# FRONT ROW - MARIA
front_row_price = sys.stdin.readline().rstrip()
# print(front_row_price) ==> 2 1 2 1

front_row_height = sys.stdin.readline().rstrip()
# print(front_row_height) ==> 2 2 1 3

# preprocess data into lists of ints
back_row_price = [int(x) for x in back_row_price.strip().split(' ')]
back_row_height = [int(x) for x in back_row_height.strip().split(' ')]
front_row_price = [int(x) for x in front_row_price.strip().split(' ')]
front_row_height = [int(x) for x in front_row_height.strip().split(' ')]

# store each tile into lists of tuples
front = list()
back = list()
for i in range(n_tiles_row):
    back.append((i, back_row_price[i], back_row_height[i]))  # tuples of (tile_num, price, height)
    front.append((i, front_row_price[i], front_row_height[i]))

# sort tiles by price first (as the price must be non-descending) then by height descending
back = sorted(back, key=lambda x: (x[1], -x[2]))
front = sorted(front, key=lambda x: (x[1], -x[2]))

# print(back) ==> [(2, 1, 4), (1, 2, 3), (3, 2, 3), (0, 3, 2)]
# print(front) ==> [(3, 1, 3), (1, 1, 2), (0, 2, 2), (2, 2, 1)]

possible_back_tile_order = list()
possible_front_tile_order = list()
for i in range(n_tiles_row):
    if back[i][2] > front[i][2]:  # if next lowest priced back tile is taller than next lowest priced front tile
        possible_back_tile_order.append(back[i][0])
        possible_front_tile_order.append(front[i][0])
    else:
        break

if len(possible_back_tile_order) < n_tiles_row:  # check that all tiles had matching pairs in back and front
    print("impossible")
else:
    print(possible_back_tile_order)
    print(possible_front_tile_order) 

【讨论】:

"在 Python 字典中对象默认未排序" -> "在 Python 3.7 之前的版本中..."(实际上是 3.6,但它是仅在 3.7 中才有保证的功能)。 一个很好的解决方案!然而,Kattis 并不认为这个解决方案是一个有效的答案。 @Jaoa 为什么这个答案在 Kattis 上不正确?我将解决方案打印为列表对象而不是字符串,所以也许这就是问题所在。 @Jaoa 将最后两行改为print(' '.join([str(x+1) for x in possible_back_tile_order])) print(' '.join([str(x+1) for x in possible_front_tile_order])) @matt123788 我不太清楚为什么它不正确。它通过了最初的几次测试,但后来失败了。代码一定有一些问题,它没有捕捉到某些情况。我也很想知道问题是什么。我想知道是否是 for 循环 for i in range(n_tiles_row): 在我们的情况下它只运行了 4 次,但在某些情况下可能需要更多。目前我不太确定,但我会继续调查。

以上是关于从 Python 中的多个列表中仅选择一个唯一元素的主要内容,如果未能解决你的问题,请参考以下文章

如何从可滚动容器中仅选择可见元素?

如何从Python中的列表中选择“x”个唯一数字?

从python中的列表中选择某些元素

从python列表中获取元素的唯一组合[重复]

如何将多个唯一元素附加到Python中的同一组列?

通过从每个集合中仅选择一个值来选择 k 个大小的子集