こんにちは、こんぶちゃ(茶コーダー)です!
初心者視点から競プロAtCoderのABC341のA問題〜C問題の自身の解答と解答説明をしていきます。
まだまだ学習の身ですので、最適コードではない部分も多々あるかと思いますが、その部分は同じ学習者として温かい気持ちで応援&ご助言頂けば嬉しいです。
では早速行ってみましょう!
今回の参加コンテスト(ABC341)
A問題:Print 341
問題文
正整数 N が与えられるので、 N 個の 0
と N+1 個の 1
からなる、0
と 1
が交互に並んだ文字列を出力してください。
解答
# 入力されたNに対して、"10"をN回繰り返し、最後に"1"を加えて出力
print("10" * int(input()) + "1")
考え方)
シンプルに “10” をN回繰り返すことにより解くことができます。最後に “1” を加え忘れることに注意。
int(input())
でユーザーから入力された正整数Nを受け取ります。"10"
をNN回繰り返します。これにより、0と1が交互にN回並んだ文字列ができます。この時点で、0はNN個、1はN個です。- 最後に
+ "1"
で文字列の末尾に1を追加します。これで1がN+1個になります。 - この結果作成された文字列を
print
関数を使って出力します。
B問題:B – Foreign Exchange
問題(要約)
空の数列Aに対して、Q個のクエリを順番に処理する問題です。
1 x
: 数列Aの末尾にxを追加する。2 k
: 数列Aの後ろからk番目の値を出力する。
解答
n = int(input()) # 国の数Nを入力
A = list(map(int, input().split())) # 各国の通貨の初期値
S, T = [], [] # Sは交換に必要な通貨の単位、Tは交換後に得られる通貨の単位
for _ in range(n-1):
s, t = map(int, input().split()) # SとTの値を入力
S.append(s)
T.append(t)
for i in range(n-1):
m = A[i] // S[i] # 国iの通貨をS[i]単位で何回交換できるか
A[i] -= S[i] * m # 国iの通貨を交換に使用
A[i+1] += T[i] * m # 国i+1の通貨を交換で獲得
print(A[-1]) # 国Nの通貨の最終的な値を出力
解き方)
国iの通貨は、国i+1の通貨に変換できるため、各国の通貨において持っている通貨の最大変換を考えることにより、最終的に国N の通貨が最大になります。
- 国の数と各国の通貨の初期値を入力します。
- 交換レート(SとT)を入力し、リストに保存します。
- 各国に対して、可能な限り通貨を隣の国の通貨と交換します。交換は、自国の通貨がS[i]以上ある場合にのみ可能です。
- 国iから国i+1への通貨交換を繰り返し、最終的に国Nの通貨が最大になるようにします。
- 最終的な国Nの通貨の単位数を出力します。
C問題:Divide and Divide
問題(要約)
H行W列のグリッドがあり、各マスは陸(.)または海(#)です。グリッドの外周は全て海です。高橋君が宇宙船で不時着し、特定の手順(T文字列)に従ってN回移動しました。移動経路上のマスはすべて陸です。高橋君が現在いる可能性のあるマスの数を求めます。
解答
h, w, n = map(int, input().split()) # グリッドのサイズと移動回数を入力
T = input() # 移動手順を入力
grid = [input() for _ in range(h)] # グリッドの状態を入力
direction = {'L': (0, -1), 'R': (0, 1), 'U': (-1, 0), 'D': (1, 0)} # 移動方向の定義
x, y = 0, 0 # 現在位置の初期化
minX, maxX = 0, 0 # x軸の移動範囲の初期化
minY, maxY = 0, 0 # y軸の移動範囲の初期化
for t in T:
dy, dx = direction[t] # 移動方向を取得
y += dy # y軸方向の移動
x += dx # x軸方向の移動
minY = min(minY, y) # y軸の最小移動範囲を更新
maxY = max(maxY, y) # y軸の最大移動範囲を更新
minX = min(minX, x) # x軸の最小移動範囲を更新
maxX = max(maxX, x) # x軸の最大移動範囲を更新
ans = 0 # 答えの初期化
for i in range(1 - minY, h - maxY):
for j in range(1 - minX, w - maxX):
if grid[i][j] != "#": # 陸であるマスのみ検討
y, x = i, j
for t in T:
dy, dx = direction[t]
y += dy
x += dx
if grid[y][x] == "#":
break # 海に当たったら中断
else:
ans += 1 # 移動経路が全て陸であればカウントアップ
print(ans) # 結果の出力
解き方)
基本的な考え方は、全検索(下記)です。
- グリッド上の海か陸を判断する
- 陸の場合は、与えられた移動手順に従う
- 与えられた移動手順が可能の場合の数を数える
ただし、全検索だと制限時間超過になってしまうため、上記から以下の考えを取り込み実行時間削減を行なっております。
- 移動可能な範囲を求め、gridの全検索範囲の縮小
- dy, dxを用いて辞書の呼び出し回数を削減
(y += direction[t][0], x += direction[t][1]を別々に呼び出したら時間超過してしまったため)
- グリッドのサイズ、移動回数、移動手順を入力します。
- グリッドの情報を読み込み、移動方向に対する影響を辞書で定義します。
- 移動手順に従って、仮想的に移動し、その過程での最小および最大のx軸、y軸の移動範囲を記録します。
- 実際にグリッド上で可能な開始位置を探索し、その位置から移動手順に従った移動が全て陸である場合のみ、高橋君が現在いる可能性のあるマスとしてカウントします。
- 最終的に、可能なマスの総数を出力します。
感想
今回は、C問題まで解けました。
A問題は文字列操作の問題であり、簡単に解くことができました。
B問題は、最終形になるまで好きな回数繰り返す問題でしたが、問題の特性をよく読むことで、リストの左から順番に操作し、繰り返しではなく割り算の商を用いることにより解くことができました。初期の回答は、割り算の商ではなく、while文を使って回数を求めていたのですが、こちらは時間超過になってしまいました。
C問題は、グリッド問題でした。全検索で問題通りに実装はできたのですが、制限時間超過になり、不正解。移動できる範囲を考えて全検索の範囲を減らし、正解に辿り着けました。
無事C問題まで解けたものの、少しだけレーティングも507ポイント(*1)に落ち込んでしまいました。徐々に落ちていってしまっていますが、ふて腐らず、地道に続けていきたいと思います。
それでは、皆さんも競プロライフを楽しんでくださいね!(*^▽^)/
*1 一時期レーティングと順位をごちゃ混ぜで書いてしまっていました。
コメント