# キーエンス プログラミング コンテスト2021 C - Robot on Grid
キーエンス プログラミング コンテスト2021 C - Robot on Grid
問題へのリンク
問題概要
H
行 W
列のマス目がある。それぞれのマスには R
, D
, X
のいずれかの文字を書き込むことができる。
すぬけ君は
残りのマスへの文字の書き込み方は
-
が(i, j) R
ならばロボットは にのみ移動できる(i, j + 1) -
が(i, j) D
ならばロボットは にのみ移動できる(i + 1, j) -
が(i, j) X
ならばロボットは ,(i, j + 1) のどちらにも移動できる(i + 1, j)
制約
ABC中の解答
少し前のコンテストで出てきた縦、横、斜めの累積和を持つDPかなと思って過去問を見たら見つけたのが ABC183 E - Queen on Grid。DPを使うのは間違いなさそうだなと思って配るDPで実装してみたけどsample 2つしか正解できず時間切れ。
H, W, K = map(int, input().split())
MOD = 998244353
cells = {}
for _ in range(K):
h, w, c = input().split()
h, w = int(h) - 1, int(w) - 1
cells[(h, w)] = c
# print(f'{cells=}')
# dp[i][j] : マス (i, j) まで移動する方法の数
dp = [[0] * (H + 1) for _ in range(W + 1)]
dp[0][0] = 1
v1 = pow(3, H * W - K - 1, MOD)
v2 = pow(3, H * W - K, MOD)
v3 = pow(3, (H * W - K) * (H - 1 + W - 1 - 1), MOD)
for i in range(H):
for j in range(W):
v = cells.get((i, j), None)
if v is None:
# D + X
dp[i + 1][j] = (dp[i + 1][j] + dp[i][j] * v1 * 2) % MOD
# R + X
dp[i][j + 1] = (dp[i][j + 1] + dp[i][j] * v1 * 2) % MOD
elif v == 'D':
dp[i + 1][j] = (dp[i + 1][j] + dp[i][j] * v2) % MOD
elif v == 'R':
dp[i][j + 1] = (dp[i][j + 1] + dp[i][j] * v2) % MOD
else: # X
dp[i + 1][j] = (dp[i + 1][j] + dp[i][j] * v2) % MOD
dp[i][j + 1] = (dp[i][j + 1] + dp[i][j] * v2) % MOD
ans = dp[H - 1][W - 1] // v3
print(ans)
公式解法1
各セルに行ける書き込み方の個数を求めて最後 X
と書かれたマスと同様に右、下のどちらにも移動可能とみなす。
この時、 X
か (R
, D
の移動方向に対応するどちらか)で書き込む必要があり、その他の通ってない空白マスはどの文字を書いても良い。すると、
計算量を下げるために
import numpy as np
H, W, K = map(int, input().split())
MOD = 998244353
cells = {}
for _ in range(K):
h, w, c = input().split()
h, w = int(h) - 1, int(w) - 1
cells[(h, w)] = c
# dp[h][w][k] := ロボットが(0, 0)から出発して空白マスをk箇所通って(h - 1, w - 1)に到達するような経路の個数
dp = np.zeros((H + W + 1, H + 1, W + 1), dtype='i')
dp[0, 0, 0] = 1
# 配るDP
for h in range(H):
for w in range(W):
v = cells.get((h, w), None)
for k in range(H + W):
if v is None:
dp[k + 1, h + 1, w] += dp[k, h, w]
dp[k + 1, h, w + 1] += dp[k, h, w]
if v in ['D', 'X']:
dp[k, h + 1, w] += dp[k, h, w]
if v in ['R', 'X']:
dp[k, h, w + 1] += dp[k, h, w]
ans = 0
for k in range(H + W + 1):
ans += dp[k, H - 1, W - 1] * pow(2, k, MOD) * pow(3, H * W - K - k)
ans %= MOD
print(int(ans))
しかし、これだと計算量が
そこで、
と、解説を読んで理解はしたが (MOD + MOD + 2) // 3
をかけていた。なるほどなー
というわけでなんとかAC
H, W, K = map(int, input().split())
MOD = 998244353
cells = {}
for _ in range(K):
h, w, c = input().split()
h, w = int(h) - 1, int(w) - 1
cells[(h, w)] = c
# dp[w][k] := ロボットが(0, 0)から出発して(h - 1, w - 1)に到達するような経路の個数, 配る際に2/3をかける
dp = [[0] * (W + 1) for _ in range(H + 1)]
dp[0][0] = 1
c = (MOD + MOD + 2) // 3 # 2/3
# 配るDP
for h in range(H):
for w in range(W):
dp[h][w] %= MOD
v = cells.get((h, w), None)
if v is None:
dp[h + 1][w] += dp[h][w] * c
dp[h][w + 1] += dp[h][w] * c
continue
if v in ['D', 'X']:
dp[h + 1][w] += dp[h][w]
if v in ['R', 'X']:
dp[h][w + 1] += dp[h][w]
ans = dp[H - 1][W - 1] * pow(3, H * W - K, MOD)
ans %= MOD
print(ans)
cf. maspyさんのコード
Discussion