Двумерные массивы

Обработка и вывод вложенных списков

Часто в задачах приходится хранить прямоугольные таблицы с данными. Такие таблицы называются матрицами или двумерными массивами. В языке программирования Питон таблицу можно представить в виде списка строк, каждый элемент которого является в свою очередь списком, например, чисел. Например, создать числовую таблицу из двух строк и трех столбцов можно так:

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

Здесь первая строка списка A[0] является списком из чисел [1, 2, 3]. То есть A[0][0] == 1, значение A[0][1] == 2, A[0][2] == 3, A[1][0] == 4, A[1][1] == 5, A[1][2] == 6.

Для обработки и вывода списка как правило используется два вложенных цикла. Первый цикл по номеру строки, второй цикл по элементам внутри строки. Например, вывести двумерный числовой список на экран построчно, разделяя числа пробелами внутри одной строки, можно так:

for i in range(len(A)):
    for j in range(len(A[i]):
        print(A[i][j], end = ' ')
    print()

То же самое, но циклы не по индексу, а по значениям списка:

for row in A:
    for elem in row:
        print(elem, end = ' ')
    print()

Естественно для вывода одной строки можно воспользоваться методом join:

for row in A:
    print(' '.join(list(map(str, row))))

Используем два вложенных цикла для подсчета суммы всех чисел в списке:

S = 0
for i in range(len(A)):
    for j in range(len(A[i])):
        S += A[i][j]

Или то же самое с циклом не по индексу, а по значениям строк:

S = 0
for row in A:
    for elem in row:
        S += elem

Создание списка

Пусть даны два числа: количество строк n и количество столбцов m. Необходимо создать список размером n×m, заполненный нулями.

Очевидное решение оказывается неверным:

A = [ [0] * m ] * n

В этом легко убедиться, если присвоить элементу A[0][0] значение 1, а потом вывести значение другого элемента A[1][0] — оно тоже будет равно 1! Дело в том, что [0] * m возвращает ccылку на список из m нулей. Но последующее повторение этого элемента создает список из n элементов, которые являются ссылкой на один и тот же список (точно так же, как выполнение операции B = A для списков не создает новый список), поэтому все строки результирующего списка на самом деле являются одной и той же строкой.

Таким образом, двумерный список нельзя создавать при помощи операции повторения одной строки. Что же делать?

Первый способ: сначала создадим список из n элементов (для начала просто из n нулей). Затем сделаем каждый элемент списка ссылкой на другой одномерный список из m элементов:

A = [0] * n
for i in range(n):
    A[i] = [0] * m

Другой (но похожий) способ: создать пустой список, потом n раз добавить в него новый элемент, являющийся списком-строкой:

A = []
for i in range(n):
    A.append([0] * m)

Ввод списка

Пусть программа получает на вход двумерный массив, в виде n строк, каждая из которых содержит m чисел, разделенных пробелами. Как их считать? Например, так:

A = []
for i in range(n):
    A.append(list(map(int, input().split())))

Или, без использования сложных вложенных вызовов функций:

A = []
for i in range(n):
    row = input().split()
    for j in range(len(row)):
        row[j] = int(row[j])
    A.append(row)

Сложный пример обработки массива

Пусть дан квадратный массив из n строк и n столбцов. Необходимо элементам, находящимся на главной диагонали, проходящей из левого верхнего угла в правый нижний (то есть тем элементам A[i][j], для которых i==j) присвоить значение 1, элементам, находящимся выше главной диагонали – значение 0, элементам, находящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n==4):

     1 0 0 0
     2 1 0 0
     2 2 1 0
     2 2 2 1

Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы A[i][j], для которых i<j, а для элементов ниже главной диагонали i>j. Таким образом, мы можем сравнивать значения i и j и по ним определять значение A[i][j]. Получаем следующий алгоритм:

for i in range(n):
    for j in range(n):
        if i < j:
            A[i][j] = 0
        elif i > j:
            A[i][j] = 2
        else:
            A[i][j] = 1

Данный алгоритм плох, поскольку выполняет одну или две инструкции if для обработки каждого элемента. Если мы усложним алгоритм, то мы сможем обойтись вообще без условных инструкций.

Сначала заполним главную диагональ, для чего нам понадобится один цикл:

for i in range(n):
    A[i][i] = 1

Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам A[i][j] для j=i+1, ..., n-1. Здесь нам понадобятся вложенные циклы:

for i in range(n):
    for j in range(i + 1, n):
        A[i][j] = 0

Аналогично присваиваем значение 2 элементам A[i][j] для j=0, ..., i-1:

for i in range(n):
    for j in range(0, i):
        A[i][j] = 2

Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:

for i in range(n):
    for j in range(0, i):
        A[i][j] = 2
    A[i][i] = 1
    for j in range(i + 1, n):
        A[i][j] = 0

А вот такое решение использует операцию повторения списков для построения очередной строки списка. i-я строка списка состоит из i чисел 2, затем идет одно число 1, затем идет n-i-1 число 0:

for i in range(n):
    A[i] = [2] * i + [1] + [0] * (n - i - 1)

Упражнения

A: Максимум

Найдите индексы первого вхождения максимального элемента. Выведите два числа: номер строки и номер столбца, в которых стоит наибольший элемент в двумерном массиве. Если таких элементов несколько, то выводится тот, у которого меньше номер строки, а если номера строк равны то тот, у которого меньше номер столбца.

Программа получает на вход размеры массива n и m, затем n строк по m чисел в каждой.

Ввод Вывод
3 4
0 3 2 4
2 3 5 5
5 1 2 3
1 2

C: Шахматная доска

Даны два числа n и m. Создайте двумерный массив размером n×m и заполните его символами "." и "*" в шахматном порядке. В левом верхнем углу должна стоять точка.

Ввод Вывод
3 4
. * . *
* . * .
. * . *

B: Снежинка

Дано нечетное число n. Создайте двумерный массив из n×n элементов, заполнив его символами "." (каждый элемент массива является строкой из одного символа). Затем заполните символами "*" среднюю строку массива, средний столбец массива, главную диагональ и побочную диагональ. В результате единицы в массиве должны образовывать изображение звездочки. Выведите полученный массив на экран, разделяя элементы массива пробелами.

Ввод Вывод
5
* . * . *
. * * * .
* * * * *
. * * * .
* . * . *

D: Диагонали, параллельные главной

Дано число n. Создайте массив размером n×n и заполните его по следующему правилу. На главной диагонали должны быть записаны числа 0. На двух диагоналях, прилегающих к главной, числа 1. На следующих двух диагоналях числа 2, и т.д.

Ввод Вывод
5
0 1 2 3 4
1 0 1 2 3
2 1 0 1 2
3 2 1 0 1
4 3 2 1 0

E: Побочная диагональ

Дано число n. Создайте массив размером n×n и заполните его по следующему правилу:

Числа на диагонали, идущей из правого верхнего в левый нижний угол равны 1.

Числа, стоящие выше этой диагонали, равны 0.

Числа, стоящие ниже этой диагонали, равны 2.

Полученный массив выведите на экран. Числа в строке разделяйте одним пробелом.

Ввод Вывод
4
0 0 0 1
0 0 1 2
0 1 2 2
1 2 2 2

F: Поменять столбцы

Дан двумерный массив и два числа: i и j. Поменяйте в массиве столбцы с номерами i и j и выведите результат.

Программа получает на вход размеры массива n и m, затем элементы массива, затем числа i и j.

Ввод Вывод
3 4
11 12 13 14
21 22 23 24
31 32 33 34
0 1
12 11 13 14
22 21 23 24
32 31 33 34

G: Симметричен ли массив?

Дано число n и массив размером n×n. Проверьте, является ли этот массив симметричным относительно главной диагонали. Выведите слово “YES”, если массив симметричный, и слово “NO” в противном случае.

Ввод Вывод
3
0 1 2
1 2 3
2 3 4
YES

H: k-я диагональ

Дан квадратный двумерный массив размером n×n и число k. Выведите элементы k-й по счету диагонали ниже главной диагонали (т.е. если k == 1, то нужно вывести элементы первой диагонали, лежащей ниже главной, если k == 2, то второй диагонали и т.д.).

Значение k может быть отрицательным, например, если k == -1, то нужно вывести значение первой диагонали лежащей выше главной. Если k == 0, то нужно вывести элементы главной диагонали.

Программа получает на вход число n, затем массив размером n×n, затем число k.

Ввод Вывод
4
1 2 3 4
5 6 7 8
0 1 2 3
4 5 6 7
1
5 1 6
4
1 2 3 4
5 6 7 8
0 1 2 3
4 5 6 7
-2
3 8

I: Транспонировать прямоугольную матрицу

Дан двумерный массив размером n×m. Симметричный ему относительно главной диагонали массив называется транспонированным к данному. Он имеет размеры m×n: строки исходного массива становятся столбцами транспонированного, столбцы исходного массива становятся строками транспонированного.

Для данного массива постройте транспонированный массив и выведите его на экран.

Ввод Вывод
3 4
11 12 13 14
21 22 23 24
31 32 33 34
11 21 31
12 22 32
13 23 33
14 24 34

J: Транспонировать квадратную матрицу

Дан двумерный массив размером n×n. Транспонируйте его и результат запишите в этот же масссив. Вспомогательный массив использовать нельзя.

Ввод Вывод
3
1 2 3
4 5 6
7 8 9
1 4 7
2 5 8
3 6 9

K: Поменять две диагонали

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

Ввод Вывод
3
1 2 3
4 5 6
7 8 9
7 2 9
4 5 6
1 8 3

L: Кинотеатр

В кинотеатре n рядов по m мест в каждом. В двумерном массиве хранится информация о проданных билетах, число 1 означает, что билет на данное место уже продано, число 0 означает, что место свободно. Поступил запрос на продажу k билетов на соседние места в одном ряду. Определите, можно ли выполнить такой запрос.

Программа получает на вход числа n и m. Далее идет n строк, содержащих m чисел (0 или 1), разделенных пробелами. Затем дано число k.

Программа должна вывести номер ряда, в котором есть k подряд идущих свободных мест. Если таких рядов несколько, то выведите номер наименьшего подходящего ряда. Если подходящего ряда нет, выведите число 0.

Ввод Вывод
3 4
0 1 0 1
1 0 0 1
1 1 1 1
2
2
3 3
0 1 0
1 0 0
1 1 1
3
0

M: Треугольник Паскаля - 1

Даны два числа n и m. Создайте массив n×m и заполните его по следующим правилам:

Числа, стоящие в строке 0 или в столбце 0 равны 1 (A[0][j] = 1, A[i][0] = 1). Для всех остальных элементов массива A[i][j] = A[i-1][j] + A[i][j-1], то есть каждый элемент равен сумме двух элементов, стоящих слева и сверху от него.

Выведите данный массив на экран, отводя на вывод каждого элемента массива ровно 6 символов (см. пример).

Ввод Вывод
4 6
     1     1     1     1     1     1
1 2 3 4 5 6
1 3 6 10 15 21
1 4 10 20 35 56

N: Треугольник Паскаля - 2

Треугольник Паскаля состоит из чисел, где каждое число равно двум числам, стоящим над ним. Если перенумеровать строки треугольника Паскаля с нуля, то i-я строка содержит i+1 число, которые равны Cij, где j - число из отрезка [0,i].

По данному числу n создайте список из n строк, где i-й элемент списка должен быть списком, содержащим i+1 число — элементы i-й строки треугольника Паскаля.

Заполните этот массив числами треугольника Паскаля. Выведите результат на экран отводя на вывод одного числа ровно 6 символов.

Ввод Вывод
5
     1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

Q: Заполнение змейкой

По данным числам n и m заполните двумерный массив размером n×m числами от 1 до n×m “змейкой”, как показано в примере. Выведите полученный массив, отводя на вывод каждого элемента ровно 4 символа.

Ввод Вывод
3 5
   1   2   3   4   5
10 9 8 7 6
11 12 13 14 15

U: Таблица умножения

Даны числа n и m. Создайте двумерый массив размером n×m и заполните его таблицей умножения по формуле A[i][j] = i * j.

Выведите получившийся массив на экран (при выводе можно использовать вложенные циклы), отводя на вывод каждого числа ровно 4 символа.

Ввод Вывод
4 6
   0   0   0   0   0   0
0 1 2 3 4 5
0 2 4 6 8 10
0 3 6 9 12 15

X: Сапер

На поле для игры в сапер клеточки с минами обозначаются символом “*”, а в каждой пустой клеточке записано число от 0 до 8, равное количеству мин в 8 клетках, соседних с данной.

Дан список мин на поле. Постройте по данному списку изображение поля.

Программа получает на вход числа N и M - количество строк и столбцов на поле, а также количество мин на поле K. Далее идет K пар чисел - координат мин. Первое число - номер строки, второе число - номер столбца.

Выведите изображение поля на экран, клетки при выводе разделяйте одним пробелом.

Тесты к этой задаче закрытые.

Ввод Вывод
3 2 2
1 1
2 2
* 2
2 *
1 1
2 2 0
0 0
0 0