Pythonで全員が集まる最適な場所の最小移動距離を求めるプログラム
問題概要
2次元の行列(グリッド)が与えられ、そこには以下のような値が含まれています。
- 0:空きセル
- 1:壁
- 2:人
人は上下左右の4方向に移動できます。このとき、壁以外のセルの中から、すべての人がそのセルまで歩く合計距離が最小になる「集合場所」を見つけ、その最小距離を求めるのが目的です。
入力例
| 2 | 0 | 1 | 0 |
| 1 | 0 | 1 | 2 |
| 0 | 0 | 2 | 2 |
この場合の出力は 7 になります。最適な集合場所は右下の角です。
解法のアプローチ
この問題は、各人の位置から幅優先探索(BFS)を実行し、グリッド上のすべてのセルへの移動コストを計算することで解けます。具体的な手順は以下の通りです。
twos(人の位置ごとのキュー)とcosts(各人から各セルへのコスト表)という2つのマップを用意します。行列の各セルを走査し、値が 2(人)であれば、その位置を
twosに登録し、costsには行列と同じサイズの無限大で初期化した2次元配列を作成します。twosの各エントリに対してBFSを実行します。- 訪問済みセルを記録するセット
seenを用意します。 - キューが空になるまで、先頭の要素
(i, j, cost)を取り出します。 - すでに訪問済みであればスキップし、未訪問なら
seenに追加してcosts[k][i][j]にコストを記録します。 - 上下左右の4方向について、行列の範囲内かつ壁でないセルをキューの末尾に
cost + 1として追加します。
- 訪問済みセルを記録するセット
ansを無限大で初期化し、すべてのセルについて各人からのコストの合計を計算し、その最小値を更新していきます。最終的な
ansを返します。
Pythonでの実装例
class Solution:
def solve(self, matrix):
twos = {}
costs = {}
for i, r in enumerate(matrix):
for j, v in enumerate(r):
if v == 2:
twos[(i, j)] = [(i, j, 0)]
costs[(i, j)] = [[1e9 for _ in matrix[0]] for _ in matrix]
for k, q in twos.items():
seen = set()
while q:
i, j, cost = q.pop(0)
if (i, j) in seen:
continue
seen.add((i, j))
costs[k][i][j] = cost
for di, dj in ((1, 0), (-1, 0), (0, 1), (0, -1)):
ni, nj = i + di, j + dj
if (ni >= 0 and nj >= 0 and ni < len(matrix) and nj < len(matrix[0]) and matrix[ni][nj] != 1):
q.append((ni, nj, cost + 1))
ans = 1e9
for i in range(len(matrix)):
for j in range(len(matrix[0])):
cur_cost = 0
for arr in costs.values():
cur_cost += arr[i][j]
ans = min(ans, cur_cost)
return ans
ob = Solution()
matrix = [
[2, 0, 1, 0],
[1, 0, 1, 2],
[0, 0, 2, 2]
]
print(ob.solve(matrix))
入力
matrix = [
[2, 0, 1, 0],
[1, 0, 1, 2],
[0, 0, 2, 2]]
出力
7
計算量について
各人の位置に対してBFSを1回ずつ実行するため、時間計算量は O(P × R × C)(Pは人数、R×Cはグリッドのサイズ)となります。空間計算量も同様に O(P × R × C) であり、人数やグリッドが大きくなるとメモリ使用量に注意が必要です。また、実装では q.pop(0) を使っていますが、パフォーマンスを重視する場合は collections.deque を使うことでキュー操作を効率化できます。
-
Pythonで全ての点を接続するための最小コストを求めるプログラム
問題の概要(x, y) の形式で表される複数の点が格納された配列 points があるとします。2つの点 (xi, yi) と (xj, yj) を接続するコストは、それらの間のマンハッタン距離として定義されます。マンハッタン距離は次の式で計算できます。|xi − xj| + |yi − yj|この問題では、すべての点を接続するために必要な最小のコストを求める必要があります。入力例points = [(0,0), (3,3), (2,10), (6,3), (8,0)]この場合、出力は 22 になります。これは、各辺の距離がそれぞれ 6 + 5 + 3 + 8 = 22 となるように点同士を接
-
Pythonで全ノードに到達可能な最小の頂点集合を見つけるプログラム
問題概要有向非巡回グラフ(DAG)を考えます。グラフにはn個の頂点があり、各ノードには0からn-1までの番号が付けられています。グラフはエッジリストとして表現され、edges[i] = (u, v)はノードuからノードvへ向かう有向エッジを意味します。このとき、そこから出発すればグラフ内のすべてのノードに到達できるような、最小の頂点集合を見つける必要があります(頂点は任意の順序で返して構いません)。例えば、入力が次のような場合を考えてみましょう。この場合、出力は [0, 2, 3] となります。これらの頂点は他のどの頂点からも到達できないため、ここから探索を開始すれば全ノードをカバーできるから