---
title: Python AtCoder入門 第８講 2次元リストとグリッド問題
tags: 
author: [佐藤](https://image.docswell.com/user/aomori_sato)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/4EMYNM25EW.jpg?width=480
description: Python AtCoder入門 第８講 2次元リストとグリッド問題 by 佐藤
published: September 21, 26
canonical: https://image.docswell.com/s/aomori_sato/ZWRN6Y-2026-09-21-133921
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/4EMYNM25EW.jpg)

Python AtCoder入門
第8講 2次元リストとグリッド問題
AtCoderのB問題には、マス目 を扱う問題がよく出ます。
迷路、盤面、地図、表。
形は違っても、正体はすべて「縦H行・横W列に並んだマス」です。
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/PER9DVMZJ9.jpg)

今回のテーマ
こうした問題を扱うための道具が、
2次元リスト
です。
リストの中にリストが入っているもの、と考えてください。
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/P7XQ1ZV1EX.jpg)

この講のポイント
新しい概念は少なめです。
使うのは主に、
第4講の二重ループ
第5講のリスト
第6講の内包表記
です。
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/37K928QM7D.jpg)

重要な罠
ただし、1つだけ知らないと必ずハマる罠があります。
[[0] * W] * H
です。
これは一見便利ですが、2次元リストでは使ってはいけません。
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LJ3W42P2J5.jpg)

コードファイル名の方針
この講でも、コード例ごとにファイル名を付けます。
grid_basic.py
bad_grid.py
char_grid.py
numeric_grid.py
neighbors.py
answer_8_1.py
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/8JDKQZL6EG.jpg)

8-1 2次元リストの基本
2次元リストは、
リストを要素に持つリスト
です。
grid = [[1, 2, 3],
[4, 5, 6]]
これは2行3列のマス目を表します。
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/VEPKLD4Z78.jpg)

grid[i][j]
要素には、
grid[i][j]
でアクセスします。
print(grid[0][0])
print(grid[1][2])
出力：
1
6
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/27VVQ1XM7Q.jpg)

iが行、jが列
grid[i][j]
の、
が行
j が列
です。
「上からi番目の行を取り出して、その中のj番目」と読みます。
どちらも0始まりです。
i
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/5JGLW2VX7L.jpg)

行数と列数
H = len(grid)
W = len(grid[0])
が行数
len(grid[0]) が列数
です。
grid は「行のリスト」だからです。
len(grid)
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/47QY3P65EP.jpg)

内包表記による初期化
すべて0で埋めたH行W列のマス目は、次のように作ります。
grid = [[0] * W for _ in range(H)]
「長さWの行を、H個作る」という意味です。
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/KE4W1Y4VJ1.jpg)

grid_basic.py
H = 3
W = 4
# Create an H x W grid filled with zeros
grid = [[0] * W for _ in range(H)]
# Set some cells
grid[0][0] = 1
grid[1][2] = 5
grid[2][3] = 9
# Print row by row
for row in grid:
print(*row)
# Size of the grid
print(len(grid), len(grid[0]))
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/L71YG644JG.jpg)

grid_basic.py の出力
1 0 0 0
0 0 5 0
0 0 0 9
3 4
出力は、
for row in grid:
print(*row)
で1行ずつ行います。
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/G7WGKWXZE2.jpg)

print(grid) は答え向きではない
print(grid)
とすると、角括弧だらけになります。
AtCoderの答えとしては、多くの場合WAです。
1行ずつ取り出して、
print(*row)
で出力しましょう。
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/4JZLZ56LE3.jpg)

[[0] * W] * H がダメな理由
次の2つは一見同じに見えます。
grid = [[0] * W for _ in range(H)]
grid = [[0] * W] * H
# 正しい
# 壊れる
下の書き方は、絶対に使ってはいけません。
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/YE6WZ92MEV.jpg)

何が壊れるのか
[[0] * W] * H
は、行をH個複製しているように見えます。
しかし実際には、
同じ1つの行への参照をH個並べているだけ
です。
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/GE5MWN2QE4.jpg)

1マス変えると全行が変わる
同じ行を何度も指しているため、
bad[0][0] = 1
とすると、すべての行の先頭が 1 になります。
第5講の「コピーの罠」と同じです。
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/9729Q54WJR.jpg)

bad_grid.py
H = 3
W = 4
# The wrong way
bad = [[0] * W] * H
bad[0][0] = 1
for row in bad:
print(*row)
print(bad[0] is bad[1])
print(&quot;---&quot;)
# The right way
good = [[0] * W for _ in range(H)]
good[0][0] = 1
for row in good:
print(*row)
print(good[0] is good[1])
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/DJY4WKM97M.jpg)

bad_grid.py の出力
1 0 0 0
1 0 0 0
1 0 0 0
True
--1 0 0 0
0 0 0 0
0 0 0 0
False
bad
では3行すべてが変わっています。
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/V7NY9RWDE8.jpg)

is の意味
bad[0] is bad[1]
は、
まったく同じものか
を調べています。
True なら、0行目と1行目が同じリストを指しているという意味です。
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/YJ9P2MX873.jpg)

鉄則
2次元リストは必ず内包表記で作ります。
grid = [[0] * W for _ in range(H)]
1次元なら [0] * W で問題ありません。
罠になるのは「リストを * で繰り返したとき」です。
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GJ8D5L2ZJD.jpg)

8-2 グリッド入力の受け取り
AtCoderでよく出るのが、 # と . などが並んだマス目です。
3 4
#..#
.##.
#..#
1行目にHとW、続くH行にマス目が与えられます。
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/LJLMYL21ER.jpg)

文字グリッドの受け取り
文字グリッドは、次の形で受け取れます。
H, W = map(int, input().split())
S = [input() for _ in range(H)]
これで S は文字列のリストになります。
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/47MYNM857W.jpg)

文字列のままアクセスできる
文字列はインデックスでアクセスできます。
print(S[0][0])
print(S[1][1])
でi行目の文字列。
その [j] でj文字目です。
2次元リストと同じ感覚で使えます。
S[i]
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/P7R9DV5ZE9.jpg)

書き換えたい場合
文字列はイミュータブルなので、書き換えられません。
マス目を書き換えたい場合は、1文字ずつのリストにします。
S = [list(input()) for _ in range(H)]
S[0][0] = &quot;.&quot;
読むだけなら文字列のままで十分です。
24


# Page. 25

![Page Image](https://bcdn.docswell.com/page/PJXQ1ZK17X.jpg)

char_grid.py
# Read the grid size
H, W = map(int, input().split())
# Read the grid as a list of strings
S = [input() for _ in range(H)]
# Count the &#039;#&#039; cells
count = 0
for i in range(H):
for j in range(W):
if S[i][j] == &quot;#&quot;:
count += 1
print(count)
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/3JK9285MJD.jpg)

char_grid.py の実行例
入力例：
3 4
#..#
.##.
#..#
出力例：
6
全マスを二重ループで見ています。
26


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LE3W42K2E5.jpg)

全マス走査が基本
グリッド問題の基本は、
for i in range(H):
for j in range(W):
# マス (i, j) について処理
です。
第4講の二重ループそのものです。
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/8EDKQZ367G.jpg)

数値グリッド
数値が空白区切りで並ぶ形式もあります。
2 3
1 2 3
4 5 6
この場合は、各行を整数リストとして受け取ります。
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/V7PKLDPZJ8.jpg)

数値グリッドの受け取り
A = [list(map(int, input().split())) for _ in range(H)]
第1講の入力テンプレートを、H行ぶん繰り返しています。
内包表記で短く書けます。
29


# Page. 30

![Page Image](https://bcdn.docswell.com/page/2JVVQ12MJQ.jpg)

numeric_grid.py
# Read the grid size
H, W = map(int, input().split())
# Read the numeric grid
A = [list(map(int, input().split())) for _ in range(H)]
# Sum of each row
for i in range(H):
print(sum(A[i]))
# Sum of each column
for j in range(W):
total = 0
for i in range(H):
total += A[i][j]
print(total)
30


# Page. 31

![Page Image](https://bcdn.docswell.com/page/5EGLW2RXJL.jpg)

numeric_grid.py の実行例
入力例：
2 3
1 2 3
4 5 6
出力例：
6
15
5
7
9
31


# Page. 32

![Page Image](https://bcdn.docswell.com/page/4JQY3PV57P.jpg)

行の合計
行の合計は簡単です。
sum(A[i])
A[i]
がi行目のリストそのものだからです。
32


# Page. 33

![Page Image](https://bcdn.docswell.com/page/K74W1YMVE1.jpg)

列の合計
列の合計は、一発では取れません。
A[0][j]
A[1][j]
A[2][j]
のように、縦にたどる必要があります。
「何を固定し、何を動かすか」を意識しましょう。
33


# Page. 34

![Page Image](https://bcdn.docswell.com/page/LJ1YG684EG.jpg)

8-3 グリッド上の探索
グリッド問題の基本は、
二重ループで全マスを見る
ことです。
for i in range(H):
for j in range(W):
# マス (i, j) について処理
34


# Page. 35

![Page Image](https://bcdn.docswell.com/page/GJWGKWZZ72.jpg)

count_cells.py
# Read the grid size and the threshold
H, W = map(int, input().split())
A = [list(map(int, input().split())) for _ in range(H)]
K = int(input())
# Count the cells whose value is K or more
count = 0
for i in range(H):
for j in range(W):
if A[i][j] &gt;= K:
count += 1
print(count)
35


# Page. 36

![Page Image](https://bcdn.docswell.com/page/4EZLZ51L73.jpg)

count_cells.py の実行例
入力例：
2 3
1 5 3
8 2 9
5
出力例：
3
5
, 8 , 9 の3つです。
36


# Page. 37

![Page Image](https://bcdn.docswell.com/page/Y76WZ9LM7V.jpg)

隣接マス
グリッド問題では、
あるマスの上下左右を調べる
処理がよく出ます。
マス (i, j) の隣は、
上： (i - 1, j)
下： (i + 1, j)
左： (i, j - 1)
右： (i, j + 1)
37


# Page. 38

![Page Image](https://bcdn.docswell.com/page/G75MWN1Q74.jpg)

方向ベクトル
上下左右の移動量をリストにまとめます。
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
これを 方向ベクトル と呼びます。
中身はタプルのリストです。
38


# Page. 39

![Page Image](https://bcdn.docswell.com/page/9J29Q51WER.jpg)

方向ベクトルを使う
for di, dj in directions:
ni = i + di
nj = j + dj
は行の変化量
dj は列の変化量
ni , nj は隣のマス
です。
di
39


# Page. 40

![Page Image](https://bcdn.docswell.com/page/DEY4WKZ9JM.jpg)

範囲外チェック
隣を見るときは、必ず範囲外チェックをします。
if 0 &lt;= ni &lt; H and 0 &lt;= nj &lt; W:
# ここで初めてアクセスしてよい
範囲外のマスにアクセスすると、エラーやWAの原因になります。
40


# Page. 41

![Page Image](https://bcdn.docswell.com/page/VJNY9R3D78.jpg)

負のインデックスに注意
Pythonでは、
S[-1]
がエラーになりません。
最後の行を指してしまいます。
範囲外チェックを忘れると、エラーが出ずに間違った答えになることがあります。
41


# Page. 42

![Page Image](https://bcdn.docswell.com/page/YE9P2M98J3.jpg)

neighbors.py
# Read the grid
H, W = map(int, input().split())
S = [input() for _ in range(H)]
# Four directions: up, down, left, right
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# For each cell, count the adjacent &#039;#&#039; cells
for i in range(H):
row = []
for j in range(W):
count = 0
for di, dj in directions:
ni = i + di
nj = j + dj
if 0 &lt;= ni &lt; H and 0 &lt;= nj &lt; W:
if S[ni][nj] == &quot;#&quot;:
count += 1
row.append(count)
print(*row)
42


# Page. 43

![Page Image](https://bcdn.docswell.com/page/GE8D5L9ZED.jpg)

neighbors.py の実行例
入力例：
3 3
.#.
###
.#.
出力例：
2 1 2
1 4 1
2 1 2
43


# Page. 44

![Page Image](https://bcdn.docswell.com/page/LELMYLW17R.jpg)

全マス × 4方向
このコードは三重ループに見えます。
しかし、いちばん内側は必ず4回です。
つまり計算量は、
H × W × 4
です。
グリッド問題で何度も使う骨格です。
44


# Page. 45

![Page Image](https://bcdn.docswell.com/page/4JMYNM95JW.jpg)

章末まとめ
2次元リストは、
リストのリスト
です。
grid[i][j]
の、
が行
j が列
です。
どちらも0始まりです。
i
45


# Page. 46

![Page Image](https://bcdn.docswell.com/page/PJR9DVGZ79.jpg)

章末まとめ：初期化
2次元リストの初期化は、
grid = [[0] * W for _ in range(H)]
です。
これは使ってはいけません。
grid = [[0] * W] * H
46


# Page. 47

![Page Image](https://bcdn.docswell.com/page/PEXQ1ZX1JX.jpg)

章末まとめ：入力
文字グリッド：
S = [input() for _ in range(H)]
書き換えるなら：
S = [list(input()) for _ in range(H)]
数値グリッド：
A = [list(map(int, input().split())) for _ in range(H)]
47


# Page. 48

![Page Image](https://bcdn.docswell.com/page/3EK928WMED.jpg)

章末まとめ：走査
グリッド問題の基本は、二重ループです。
for i in range(H):
for j in range(W):
# マス (i, j)
全マスを1つずつ見ます。
48


# Page. 49

![Page Image](https://bcdn.docswell.com/page/L73W421275.jpg)

章末まとめ：隣接マス
上下左右は方向ベクトルで扱います。
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
範囲外チェックを必ず書きます。
if 0 &lt;= ni &lt; H and 0 &lt;= nj &lt; W:
49


# Page. 50

![Page Image](https://bcdn.docswell.com/page/87DKQZX6JG.jpg)

練習問題 8-1 黒いマスの数
H行W列のマス目があります。
各マスは、
# ：黒
. ：白
です。
黒いマスの個数を出力してください。
50


# Page. 51

![Page Image](https://bcdn.docswell.com/page/VJPKLDKZE8.jpg)

練習問題 8-1：入力と出力
入力：
H W
S_1
S_2
...
S_H
入力例：
3 4
#..#
.##.
#..#
出力例：
6
51


# Page. 52

![Page Image](https://bcdn.docswell.com/page/2EVVQ1VMEQ.jpg)

answer_8_1.py
# Read the grid
H, W = map(int, input().split())
S = [input() for _ in range(H)]
# Count the &#039;#&#039; cells
count = 0
for i in range(H):
for j in range(W):
if S[i][j] == &quot;#&quot;:
count += 1
print(count)
全マス走査とカウントパターンです。
52


# Page. 53

![Page Image](https://bcdn.docswell.com/page/57GLW2LXEL.jpg)

answer_8_1_count.py
# Read the grid
H, W = map(int, input().split())
S = [input() for _ in range(H)]
# Count &#039;#&#039; in each row and sum them up
print(sum(row.count(&quot;#&quot;) for row in S))
文字列の count() を使うと短く書けます。
53


# Page. 54

![Page Image](https://bcdn.docswell.com/page/4EQY3PY5JP.jpg)

練習問題 8-2 行と列の最大合計
H行W列の数値が並んだ表があります。
各行の合計の最大値と、各列の合計の最大値を、この順に空白区切りで出力してください。
54


# Page. 55

![Page Image](https://bcdn.docswell.com/page/KJ4W1YWV71.jpg)

練習問題 8-2：入力と出力
入力例：
2 3
1 2 3
4 5 6
出力例：
15 9
行の最大合計は 15 。
列の最大合計は 9 です。
55


# Page. 56

![Page Image](https://bcdn.docswell.com/page/LE1YG6Y47G.jpg)

answer_8_2.py
# Read the numeric grid
H, W = map(int, input().split())
A = [list(map(int, input().split())) for _ in range(H)]
# Sum of each row
row_sums = []
for i in range(H):
row_sums.append(sum(A[i]))
# Sum of each column
col_sums = []
for j in range(W):
total = 0
for i in range(H):
total += A[i][j]
col_sums.append(total)
print(max(row_sums), max(col_sums))
56


# Page. 57

![Page Image](https://bcdn.docswell.com/page/GEWGKWGZJ2.jpg)

行の合計は内包表記でも書ける
row_sums = [sum(A[i]) for i in range(H)]
行は A[i] でそのまま取り出せます。
列は縦にたどる必要があります。
57


# Page. 58

![Page Image](https://bcdn.docswell.com/page/47ZLZ5LLJ3.jpg)

練習問題 8-3 孤立した黒マス
H行W列のマス目があります。
黒いマスのうち、
上下左右のいずれにも黒いマスが隣接していない
ものを「孤立している」と呼びます。
孤立している黒いマスの個数を出力してください。
58


# Page. 59

![Page Image](https://bcdn.docswell.com/page/YJ6WZ9WMJV.jpg)

練習問題 8-3：入力と出力
入力例：
3 4
#..#
.##.
#..#
出力例：
4
四隅の4つの # が孤立しています。
59


# Page. 60

![Page Image](https://bcdn.docswell.com/page/GJ5MWNMQJ4.jpg)

answer_8_3.py
# Read the grid
H, W = map(int, input().split())
S = [input() for _ in range(H)]
# Four directions: up, down, left, right
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# Count isolated &#039;#&#039; cells
answer = 0
for i in range(H):
for j in range(W):
if S[i][j] != &quot;#&quot;:
continue
neighbors = 0
for di, dj in directions:
ni = i + di
nj = j + dj
if 0 &lt;= ni &lt; H and 0 &lt;= nj &lt; W:
if S[ni][nj] == &quot;#&quot;:
neighbors += 1
if neighbors == 0:
answer += 1
print(answer)
60


# Page. 61

![Page Image](https://bcdn.docswell.com/page/LE3W42WZE5.jpg)

answer_8_3.py のポイント
白いマスは判定する必要がありません。
if S[i][j] != &quot;#&quot;:
continue
で次のマスへ進みます。
隣接する黒マスの数が 0 なら、孤立しています。
61


# Page. 62

![Page Image](https://bcdn.docswell.com/page/8EDKQZK47G.jpg)

第8講まとめ
この講では、
2次元リストとグリッド問題
を学びました。
B問題では、マス目を二重ループで走査する問題がよく出ます。
62


# Page. 63

![Page Image](https://bcdn.docswell.com/page/V7PKLDKVJ8.jpg)

次回予告
次の第9講では、
関数と組み込み関数
を扱います。
ここまで書いてきた処理に名前を付けて整理する方法と、Pythonが用意している便利な関数を学びます。
63


