D - 画像回転エディタ / Image Rotation Editor 解説 by admin
Claude 4.5 Opus概要
\(N \times N\) の画像に対して、指定された \(K \times K\) の正方形領域を時計回りに90度回転させる操作を \(Q\) 回行い、最終的な画像の状態を求める問題です。
考察
問題の理解
まず、「時計回りに90度回転」とは何かを具体例で確認しましょう。
\(3 \times 3\) の領域を考えます:
回転前: 回転後:
1 2 3 7 4 1
4 5 6 → 8 5 2
7 8 9 9 6 3
左端の列(1, 4, 7)が上端の行(7, 4, 1)になり、真ん中の列が真ん中の行に、右端の列が下端の行になります。
座標変換の導出
回転前の座標 \((i, j)\) にある要素が、回転後にどこへ移動するかを考えます。
- 回転前: \((i, j)\) の要素
- 回転後: \((j, K-1-i)\) の位置へ移動
逆に言えば、回転後の位置 \((i, j)\) には、回転前の \((K-1-j, i)\) の要素が来ます。
計算量の確認
この問題では、制約に \(Q \times K^2 \leq 2 \times 10^7\) が与えられています。これは「すべての回転操作で扱うピクセルの総数」の上限を示しています。
素朴に各操作で \(K \times K\) 個のピクセルを処理しても、全体で \(O(Q \times K^2) = O(2 \times 10^7)\) となり、十分間に合います。
アルゴリズム
画像の読み込み: \(N \times N\) の画像を2次元リストとして保持する
回転操作の実装: 各操作について以下を行う
- 回転する \(K \times K\) 領域を一時的な配列にコピーする
- 座標変換の式を使って、回転後の位置に書き戻す
結果の出力: 最終状態の画像を出力する
回転の実装詳細
左上が \((r, c)\) の \(K \times K\) 領域を回転させるとき:
回転後の (r+i, c+j) = 回転前の (r+K-1-j, c+i)
これは領域内のローカル座標で考えると: - ローカル座標 \((i, j)\) には、回転前のローカル座標 \((K-1-j, i)\) の値が来る
計算量
時間計算量: \(O(N^2 + Q \times K^2)\)
- 画像の読み込みに \(O(N^2)\)
- 各回転操作で \(K^2\) 個のピクセルを処理し、これを \(Q\) 回行うので \(O(Q \times K^2)\)
- 制約より \(Q \times K^2 \leq 2 \times 10^7\) なので十分高速
空間計算量: \(O(N^2 + K^2)\)
- 画像の保持に \(O(N^2)\)
- 回転時の一時領域に \(O(K^2)\)
実装のポイント
0-indexed への変換: 問題文では座標が1始まりですが、プログラム内では0始まりで扱う方が便利です。入力された座標 \((R, C)\) を \((R-1, C-1)\) に変換します。
一時配列へのコピー: 回転操作では、元の領域を読みながら同じ領域に書き込むと、まだ読んでいない値が上書きされてしまいます。そのため、まず領域全体を一時配列にコピーしてから書き戻します。
座標変換式の正確な実装: 時計回り90度回転の式は間違えやすいので、小さな例で手計算して確認するとよいでしょう。
ソースコード
import sys
def rotate_region(grid, r, c, k):
"""K×K領域を時計回りに90度回転させる"""
# 回転前の領域を取得
region = []
for i in range(k):
row = []
for j in range(k):
row.append(grid[r + i][c + j])
region.append(row)
# 時計回りに90度回転させて書き戻す
# 回転後: new[j][k-1-i] = old[i][j]
# つまり: new[i][j] = old[k-1-j][i]
for i in range(k):
for j in range(k):
grid[r + i][c + j] = region[k - 1 - j][i]
def main():
input_data = sys.stdin.read().split('\n')
idx = 0
n, k, q = map(int, input_data[idx].split())
idx += 1
# 画像を2次元リストとして読み込む
grid = []
for i in range(n):
grid.append(list(input_data[idx]))
idx += 1
# Q回の操作を実行
for _ in range(q):
r, c = map(int, input_data[idx].split())
idx += 1
# 0-indexedに変換
rotate_region(grid, r - 1, c - 1, k)
# 結果を出力
for row in grid:
print(''.join(row))
if __name__ == "__main__":
main()
この解説は claude4.5opus によって生成されました。
投稿日時:
最終更新: