从 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: 4
或 1: 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 / 2
、n!
对于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 中的多个列表中仅选择一个唯一元素的主要内容,如果未能解决你的问题,请参考以下文章