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

时间:2019-10-30 23:49:17

标签: python python-3.x python-2.7 list

这不是我正在努力做的作业,但我正在尝试解决问题(如果有兴趣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]}

请告诉我是否有其他方法可以解决此问题。

2 个答案:

答案 0 :(得分:0)

解决问题的一种可能无效的方法是生成所有可能的“解决方案”(其值可能在与特定键相对应的列表中不存在)并满足“有效”的要求(所有值都适用)出现在相应的列表中。

使用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个笛卡尔乘积解决方案。

答案 1 :(得分:0)

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

这里的技巧是先按价格升序(要求不降序)然后按高度降序排列磁贴,以使后排价格第二低的最高磁贴与该行中的最高磁贴匹配。前排的下一个最低价格。 为此,我利用了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)