AtCoder Beginner Contest 210をPythonで解く

投稿日: 

Atcoder Python

B!
Daniel AgreloによるPixabay(https://pixabay.com/)からの画像

こんにちは, Shinoryoです.

今回はAtCoder Beginner Contest 210を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Cabbages

$N \leq A$の場合, 全てのキャベツが$1$個$X$円で購入できるので, 合計金額は$NX$円です.

$N > A$の場合, $A$個のキャベツが$1$個$X$円で購入出来て, $N - A$個のキャベツが1個$Y$円で購入できるので, 合計金額は$AX + (N-A)Y$円です.

以上から, どちらの場合も合計金額を$\mathrm{min} \, (N, A) \times X + \, \mathrm{max} \, (0, N-A) \times Y$円と書けます.

B - Bouzu Mekuri

文字列$S$の先頭から見て初めて$1$が現れる場所を$X$文字目とします(ただし, 先頭から$0$文字目, $1$文字目と数えることとし, これはインデックスにそのまま一致します). $X$が偶数ならばそのカードを高橋君が食べ, $X$が奇数ならばそのカードを青木君が食べることになります.

文字列$S$にある文字が含まれるかどうかを探索するには, .find()を使用することができます. .find()を使う場合, 先頭から探索します.

.find()を使わなくても, 明示的に探索の操作を行えばよいです.

C - Colorful Candies

1つの区間に含まれるキャンディの色の種類数を求めるには 「区間内の$K$個のキャンディを一つずつ見て, 区間内の色の種類数を数える」という方法が考えられます. しかし, これだと時間計算量に$O(K(N-K+1))$になり, TLEです.

そこで, 「各$i = 1,2, \ldots, N - K$に対する$[i, i + K - 1]$と$[i + 1, i + K]$のキャンディの純粋に差分を確認する」という方法を考えます. 辞書型を用いて$\{1: \mathrm{number}(1), 2: \mathrm{number}(2), 3: \mathrm{number}(3), \ldots \}$のように各キャンディの個数を記録し, それを変化させていく方法を考えます. また, キャンディの種類数を別に記録しておけば, 辞書型のvalueを合計する時間計算量を減らすことができます.

D - National Railway

この問題は駅$(i_s, j_s)$から駅$(i_f, j_f)$までの鉄道を敷設するのにかかる最小の値段を動的計画法(dynamic programming)を用いて考えていきます.

その際, $i_s <= i_f$は一般に仮定してよいことに注意してください. 駅$(i_s, j_s)$から駅$(i_f, j_f)$までの鉄道を敷設するのにかかる最小の値段は, 駅$(i_f, j_f)$から駅$(i_s, j_s)$までの鉄道を敷設するのにかかる最小の値段と同一であることから, そのように仮定してよいことが分かります.

また, $j_s <= j_f$も仮定します. $j_s >= j_f$のパターンは, 例えば行列$A_{i, j}$の左右を反転させることによって実現できます. (これに関しては, $A_{i, j}$の左右を反転させたものに対して同様の作業を繰り返さなければならないことに注意してください. )

中間地点$(i, j)$を境に2段階に分けて考えると楽になります.

  • 青木君が一方の駅をすでに建設して現在$(i,j)$にいるとき, それまでにかかった費用として考えられる最小値(dp[i][j])
    1. $(i, j)$に駅を立てた
    2. $(i-1, j)$から$(i,j)$に線路を引いて移動してきた
    3. $(i, j-1)$から$(i,j)$に線路を引いて移動してきた
  • 2つ目の駅を$(i, j)$に建てて青木君がどこかへ飛び立ったとき, そこまでにかかった費用として考えられる最小値(X[i][j])
    1. $(i-1, j)$から$(i,j)$に線路を引いて移動してきて, $(i,j)$に駅を立てた
    2. $(i, j-1)$から$(i,j)$に線路を引いて移動してきて, $(i,j)$に駅を立てた

$A_{i, j}$の左右反転には, 例えば.reverse()を用いると便利です.

多次元配列の最小値を求めるには, 例えば多次元リストの平坦化を用いると便利です. Pythonには, itertoolsモジュールのitertools.chain.from_iterable()があり, itertools.chain.from_iterable(matrix)のような形で, 2次元配列を1次元配列にすることができます.

なお, Python (3.8.2)で実行するとTLEになります. PyPy3 (7.3.0)で実行することでACです.

E以降

E以降は私の能力不足故に省略いたします.

参考にしたサイト等