目次

AtCoder Beginner Contest 184 C,D,E,F問題メモ

AtCoder Beginner Contest 184

個人的に、今回は骨のあるABCだった。

C - Super Ryuma

C - Super Ryuma

問題

解法

0手は言うまでも無し。1手は問題文中に判定法があるのでその通りに。

3手かければ絶対行けるので、「2手か3手か」というのがメインの考察。

以下、1手以下ではいけないとする。以下のパターンで2手が可能、それ以外で3手となる。

ただし、マンハッタン距離3以下の移動を“横移動”と称する。

2手のナナメ移動

「市松模様に塗ったときに同じ色になるマス」へはどんなに離れていても行ける。
$r_1+c_1$ と $r_2+c_2$ の偶奇が一致していればよい。

        ・
      ↗  ↘
    ↗      ↘
  ↗         (r2,c2)
(r1,c1)

なお、3手で絶対行けるのは、2手であと1マスの距離まで近づけることから分かる。

2手の横移動

マンハッタン距離が6以下ならよい。

1手の横移動 + 1手のナナメ移動

これが少しややこしい。

1手目で $(r_2,c_2)$ のナナメの利きに移動できる場合も2手で移動できる。
この場合、$(r_1,c_1)$ から見て $(r_2,c_2)$ が幅のあるナナメの範囲に入っているか、という判定となる。

$(r_1,c_1)$ を $(0,0)$ に平行移動すると、少しわかりやすくなる。

o: 1手横移動 + 1手ナナメ移動(よりも少ない手数)で行ける範囲

    -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6
-6   o  o  o  o                 o  o  o  o
-5   o  o  o  o  o           o  o  o  o  o
-4   o  o  o  o  o  o     o  o  o  o  o  o
-3   o  o  o  o  o  o  o  o  o  o  o  o  o
-2      o  o  o  o  o  o  o  o  o  o  o
-1         o  o  o  o  o  o  o  o  o
 0            o  o  o  o  o  o  o
 1         o  o  o  o  o  o  o  o  o
 2      o  o  o  o  o  o  o  o  o  o  o
 3   o  o  o  o  o  o  o  o  o  o  o  o  o
 4   o  o  o  o  o  o     o  o  o  o  o  o
 5   o  o  o  o  o           o  o  o  o  o
 6   o  o  o  o                 o  o  o  o

これは、$r,c$ の関係性を見ると、「差か和の絶対値が3以下」なら行ける、ということになる。

もう1つの方法として、$(r_1,c_1)$ から1手の横移動で行けるマスを全て試して、「あと1手のナナメ移動で $(r_2,c_2)$ へ行けるか?」を調べてもよい。

Python3

なお、本番はマンハッタン距離6以下を見落とす嘘解法で通した模様。

D - increment of coins

D - increment of coins

問題

解法

状態数が少ない ($100^3$)ので、再帰を利用した全探索。

すると、たとえば $\dfrac{a}{a+b+c}$ の確率で $a$ 枚あるコインが選ばれ、その時の操作回数は $f(a+1,b,c)+1$ 回となる。

これを $b,c$ 枚のコインについてもあわせればよい。

再帰で解く場合はキャッシュを忘れないように。

Python3

E - Third Avenue

E - Third Avenue

問題

解法

よくあるグリッド探索に、ワープ移動も追加されたもの。

注意点として、ある文字のワープを1回使ったら、同じ文字のワープを2回目に使う意味は無い。

グリッド探索は基本的にグラフを構築して幅優先探索に帰着させるのだが、移動できるマス間全てに辺を張ってしまうと、 「'S'と'G'以外の全てのマスが同じアルファベット」などのケースで辺数が $O(H^2W^2)$ となり、MLE or TLEとなる。

以下の方法で回避できる。

本番ではNumbaを使ったが、使わない素のPythonでも高速化を頑張ればギリギリ通った。
文字列が入力にある問題でのNumbaは変換をどうしようか悩む時間がもったいないですね。 (ただ、素のPythonでTLEを解消しようと高速化頑張るよりはよほどまし)

Python3

F - Programming Contest

F - Programming Contest

問題

解法

制約を見て、ペロッ…これは…半分全列挙!! となる。

やりたいことはナップサックだが、$O(TN)$ かかるので無理。
bitDPも $O(2^N)$ なのでちょと厳しい。

ナップサック問題はそれくらいしか現実的な解法が無いはずなので、困る。

$O(2^\frac{N}{2})$ なら可能な制約であることに着目する。

$N$ 個を半分くらいに分け($B,C$ とする)、それぞれでbitDPにより「作れる所要時間の集合」$D_B,D_C$ を求める。

A = { 2 3 5 7 11 }

B: 2 3 5  →  DB: { 0 2 3 5 7 8 10 }  2,3,5だけで作れる所要時間
C: 7 11   →  DC: { 0 7 11 18}        7,11だけで作れる所要時間

すると、「$D_B$ と $D_C$ から1つずつ選んで足し合わせて作れる値」が「$A$ の $N$ 個の要素を選んだり選ばなかったりして作れる値」と一致する。

ここで $D_C$ の1要素 $d$ を固定すると、$D_B$ からは「$T-d$ を超えない最大の値」を持ってきてペアにするのが最適となる。
これはあらかじめ $D_B$ をソートしておくと、二分探索で高速に持ってこれる。

T = 16

{ 0 7 11 18 } から 7 を固定すると...
{ 0 2 3 5 7 8 10 } からは、9を超えない、9に最も近い値が欲しい

なので、$D_C$ の $2^\frac{N}{2}$ 個の要素それぞれにつき、$D_B$ の二分探索をすることで答えの候補を求められる。

その中で最大のものが答え。

計算量は、$O(2^\frac{N}{2}N)$ となる。

なんか「競プロで、解くのにかかる時間をなるべく制限時間ぎりぎりに近づける」という問題設定が直感的でなくって、 金額とか、暇な時間をつぶすとか、他のシチュエーションの方が頭に入ってきやすかったなあとちょっと思った。

まぁ、金額だとありふれすぎて、丸被りしちゃう過去問があったのかも。

Python3

高速化

計算量上は、以下の2箇所を工夫することで、$O(2^\frac{N}{2})$ に減らせるらしい。(元の方法では、どちらも $O(2^\frac{N}{2}N)$ かかっている)

が、実際にやってみると、以下の理由からか、あまり速くはならない。

Numpyを使うと以下のことが可能になる。

すると、Numbaを使わないPythonでも、200msを切ることが可能となる。