Как можно поместить все камни в матрицу

Весь код внизу.

У меня есть определенные фигуры, определенного цвета.

Например, фигура цветом 6 имеет форму квадрата 2х2

[6, 6, 0, 0, 0]
[6, 6, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]

Фигура цветом 5 имеет форму

[5, 5, 0, 0, 0]
[5, 5, 5, 0, 0]
[5, 5, 5, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]

(все остальные фигуры увидите если запустите код).

На самом деле у меня txt файл

5
5
6 0 4 0 5 -1 5 -1 4
5 4 2 4 1 4 0 3 1 3 2 3 0 2 1 2 0
2 6 4 7 4 7 3
3 -2 9 -3 9
4 7 10 8 10 8 11 9 10 6 10 5 10
1 4 14 4 13

Первые две строки показывают размер матрицы, в которую надо поместить все фигуры. Первый столбец каждой строки показывает цвет фигуры, а следом идущие числа - это координаты в матрице, такие, что в конце получится фигура.

def readStones(filename):
    stones = []
    f = open(filename,"r")
    numRows = int(f.readline().strip())  #1 ряд файла
    numCols = int(f.readline().strip())  #2 ряд файла
    for line in f:
        numbers = list(map(int, line.strip().split()))  #все числа после этих двух рядов
        color = numbers[0]                              #первый столбец это цвета
        coords = numbers[1:]                            #r1 c1 ... rn cn
        cells = []                                      #[r1,c1 ... rn,cn] = [[r1,c1],...,[rn,cn]]
        for i in range(len(coords)//2):
            cells.append( [ coords[2*i+0], coords[2*i+1] ] )
        stones.append( [ color, cells ] )
    f.close()
    return numRows, numCols, stones;


def shape_of_stones(numRows, numCols, stones, n):
    '''find out the shape of the stones '''
    matrix_for_shape = [0] * numRows
    for k in range(numRows):
        matrix_for_shape[k] = [0] * numCols
    rows = [] 
    cols = []
    for cellindex in range(len(stones[n][1])):
        row, col = stones[n][1][cellindex]
        rows.append(row)
        cols.append(col)
    for cell in range(len(stones[n][1])):
        r, c = stones[n][1][cell]
        i = stones[n][0]
        matrix_for_shape[r - min(rows)][c - min(cols)] = i
    print(*matrix_for_shape, sep='\n')

def main():
    M,N, stones = readStones("stones.txt")
 
    #информация о камнеstones[n]
    #его цвет stones[n][0], его координаты stones[n][1]    

    for n in range(len(stones)):
        shape_of_stones(M, N, stones, n)
        print('--------'*2)
main()

Не могу придумать рекурсивную функцию, которая бы поместила все фигуры.

Вот как можно собрать все эти фигуры

[4, 5, 5, 1, 1]
[4, 5, 5, 5, 3]
[4, 5, 5, 5, 3]
[4, 4, 2, 6, 6]
[4, 2, 2, 6, 6]

Как по мне, то лучше начинать с камней, которые имеют наибольшую площадь. (по мере выполнения, буду добавлять код)


Ответы (2 шт):

Автор решения: GrAnd

Если без вращения фигур, то, например, можно так.

import copy

def readStones(filename):
    stones = {}
    with open(filename,"r") as f:
        numRows = int(f.readline().strip())  #1 ряд файла
        numCols = int(f.readline().strip())  #2 ряд файла
        for line in f:
            numbers = list(map(int, line.strip().split()))  #все числа после этих двух рядов
            color = numbers[0]                              #первый столбец это цвета
            coords = numbers[1:]                            #r1 c1 ... rn cn
            cells = [coords[i:i + 2] for i in range(0, len(coords), 2)]  #[r1,c1 ... rn,cn] = [[r1,c1],...,[rn,cn]]
            stones[color] = cells
    return numRows, numCols, stones;


def stone_normalize(coords, color=None):
    '''find out the shape of the stones '''
    rows, cols = zip(*coords)
    min_row, max_row = min(rows), max(rows)
    min_col, max_col = min(cols), max(cols)
    height = max_row - min_row + 1
    width  = max_col - min_col + 1
    norm_rows = map(lambda row: row - min_row, rows)
    norm_cols = map(lambda col: col - min_col, cols)
    norm_coords = list(zip(norm_rows, norm_cols))
    if color is not None:  # print figures just for debug
        matrix_for_shape = [[0]*width for _ in range(height)]
        for row, col in norm_coords:
            matrix_for_shape[row][col] = color
        print(*map(" ".join, (map(lambda x: str(x) if x else " ", row) for row in matrix_for_shape)), sep="\n")
    return (width, height, norm_coords)

def place_stone(stones, n_rows, n_cols, field=None):
    if not stones:     # всё разместили, выходим
        return field
    if field is None:  # создаём поле при первом вызове
        field = [[0]*n_cols for _ in range(n_rows)]
    for color in stones:
        rest_stones = stones.copy()  # оставшиеся камни
        width, height, coords = rest_stones.pop(color)
        for row in range(n_rows - height + 1):
            for col in range(n_cols - width + 1):
                if all(field[row+r][col+c] == 0 for r, c in coords): # можно разместить
                    new_field = copy.deepcopy(field)
                    for r, c in coords:
                        new_field[row+r][col+c] = color
                    result = place_stone(rest_stones, n_rows, n_cols, new_field)
                    if result: 
                        return result

def main():
    ROWS,COLS, stones = readStones("210.txt")
 
    #информация о камне stones[color]
    #ключ - его цвет, значение - его размер и координаты его частей

    for color in stones:
        stones[color] = stone_normalize(stones[color], color)
        print('-'*10)

    # сортируем камни от большего к меньшему
    #stones = {k: v for k, v in sorted(stones.items(), key=lambda item: len(item[1][-1]), reverse=True)}
    stones = {k: v for k, v in sorted(stones.items(), key=lambda item: (item[1][0]*item[1][1], len(item[1][-1])), reverse=True)}
    result = place_stone(stones, ROWS, COLS)
    if result:
        print(*map(" ".join, (map(str, row) for row in result)), sep="\n")
    else:
        print("Невозможно разместить!")
    
main()
→ Ссылка
Автор решения: SergFSM

у меня получилось очень похожее решение (видимо потому что использовал ту же логику):

with open('matr.txt') as f:
    n_row = int(f.readline().strip())
    n_col = int(f.readline().strip())
    st = [[*map(int, l.split())] for l in f.readlines()]

def tetr(matr, stones):
    if not stones: return matr
    c, *coords = stones.pop()
    f = lambda x,y: [i-y for i in x]  # определяем форму (положение) камней
    row, col = f(coords[::2],min(coords[::2])), f(coords[1::2],min(coords[1::2]))
    
    for x in range(n_row-len(set(row))+1):
        for y in range(n_col-len(set(col))+1):
            if all(matr[i+x][j+y]==0 for i,j in zip(row,col)):
                matr2 = [m.copy() for m in matr]
                for i,j in zip(row,col):
                    matr2[i+x][j+y] = c
                res = tetr(matr2, stones.copy())  # рекурсивно перебираем комбинации
                if res: return res
    return None

field = [[0]*n_col for _ in range(n_row)]  # исходное поле
res = tetr(field, st)

print(*res or ['нет решения'], sep='\n')

результат:

[4, 5, 5, 1, 1]
[4, 5, 5, 5, 3]
[4, 5, 5, 5, 3]
[4, 4, 2, 6, 6]
[4, 2, 2, 6, 6]
→ Ссылка