人工知能と競プロやってくブログ

深層学習・機械学習・AI・atcoder・競技プログラミングについて調べてやってみたことをまとめるブログです

AtCoder Beginner Contest 162 [ D - RGB Triplets ]をPythonで解く(400点、🟫茶diff)

問題

atcoder.jp

解法

1 <= N <= 4000
なのでO(N3)だと、43 * 109 でTLEのはず。
なので、R, Gの二つの要素を固定。
すると O(16 * 106) くらい?
あとは、計算でBの要素から何個採用できるかを出す。
この方針でTLEは出ないはず。

R, Gのある場所のインデックスは総当たり。
R, Gのある場所のインデックスの大きい方と小さい方の変数をbig, smallとする。

I < j < k であるから、このbig, smallは
1. small = i, big = j
2. small = i, big = k
3. small = j, big = k
の3通りが想定できる。
j-i != k-j である必要があるので、採用しない要素があるかをBのインデックスから確認する。

採用しない要素の数が出たら
Bの数 - 採用しない要素の数
を足し込んでいく。

実装

実行結果

PyPy3(7.3.0) AC 315ms
Python3.8.2 AC 1967ms お、ギリやな

参考

uchidama.hatenablog.com

この問題が似てた。

AtCoder Beginner Contest 143 [ D - Triangles ]をPythonで解く(400点、🟫茶diff)

問題

atcoder.jp

解法その1 numpy + njitを使ったO(N3)解法 

なんと、何も考えずに問題通り、総当たりコードを書くとACしてしまうという解法。
マジですか。

AtCoderPythonではnumpyが使用できるが、njitを使って関数の実行速度を高速化。
実行すると、1731msくらいでACする。

問題の制約が、3 < N < 2 * 103
とすると、O(N3)は、(2 * 103)3なので、8 * 109
108をオーバーするので、普通はTLEだろうと見込んでスルーする実装だが、njitを使うことで実は間に合ってしまう。

なるほどねー。わからないから、とりあえずnjitでも使ってみるか。
という選択肢もアリってことかな?

とりあえず作問した人は想定してなかった解答だろうな。

実装その1

解法その2

問題の制約が、3 < N < 2 * 103
全探索は109オーバーなので、数学的な最適化を狙う方法。
atcoderの通常パターンなら、こう考えるべきだがw

a < b < c と考える。
a, bに総当たりならば(2 * 103)2 = 4 * 106
cの値は計算で求める。
c < a + b
であるあから、a + b以下の要素がLの中に何個あるかを二分探索で求める。
cはbより大きいから、b+1以上の要素が下限。

Lの中からa+b以下、b+1以上の要素数を引き算で出せば終了。
計算量はO( N2 logN)

実装その2

参考

atcoder.jp

youtu.be

https://img.atcoder.jp/abc143/editorial.pdf

AtCoder Beginner Contest 144 [ D - Water Bottle ]をPythonで解く(400点、🟫茶diff、図形問題)

問題

atcoder.jp

数学問題。

解法

水の体積が、水筒の半分以下か以上かで処理が異なる。

水の体積が水筒の半分以下のとき

半分以下の時、水は三角柱の形でこぼれる。

f:id:uchidamax:20210805233400p:plain
水の体積が半分以下

θの角度が求める答え。

水の面積 x / a
水の底辺yの値を求める。三角形の面積の公式から
x / a = b * y /2
これをyについて解く。両辺に2をかけて
2 * x / a = b * y
y = 2 * x / (a * b)

ここまでとけたら、atanを計算すればθは出る。
atan(b / y)

水の体積が水筒の半分以上のとき

半分以下の時、水は台形でこぼれる。

f:id:uchidamax:20210805233733p:plain
水の体積が半分以上
θの角度が求める答え。

水の面積 x / a
水筒の面積 a * b
水筒の空気部分の面積 a * b - x / a (水筒の面積 - 水の面積)

水筒の空気部分の三角形の辺yの長さを求める。
三角形の面積の公式から
a * b - x / a = a * y / 2
これをyについて解く
2 * a * b - 2 * x / a = a * y
y = 2 * b - 2 * x / a2

ここまでとけたら、atanを計算すればθは出る。
atan(y / a)

実装

参考

blog.hamayanhamayan.com

https://img.atcoder.jp/abc144/editorial.pdf

Educational DP Contest / DP まとめコンテスト [ K - Stones ]をPythonで解く

問題

atcoder.jp

解法

K個の石の山に対して、

dp[k] := k個の石からなる山で先手が勝ち状態か(=1なら勝ち状態)

というdpを設計。

数が小さい方から、Kまでの石の山をシミュレーションしてdpの数値を埋めていく。
まずはk = 0と非常に単純な状態から調べていく。

dp[0] = 0 ならば、先手負けとなる。
k > 0以上のケースで、次がdp[0]に遷移するならば、dp[0] = 0で、そこで行動する手番(つまり後手)は負けとわかる。

実装

基本、はまやん氏のC++コードをPythonコンバート。
細かく調整。

参考

blog.hamayanhamayan.com

AtCoder Beginner Contest 141 [ D - Powerful Discount Tickets ]をPythonで解く(400点、🟩緑diff)

問題

atcoder.jp

解法

問題を一見すると
「どの商品に何枚半額割引券を使えばよいか?」
という問題に見える。

だが半額割引券を1枚ずつ使っても結果は変わらない。

なので、
「一番値段が高い商品に1枚ずつ割引券を使う」
という貪欲法で解ける。

あとは、これを高速に処理すれば良いが、優先度付きキュー(priority queue)を使う。
優先度付きキューは、最小値を高速にとることができるので、全ての数字に-1をかけて負の数字として扱い、最小値を半額していくロジックにする。

負の値を//2で割り算切り捨てすると結果にズレが出るので正の値で//2にする。

実装

参考

youtu.be

qiita.com

AtCoder Beginner Contest 212 [ D - Querying Multiset ]をPythonで解く(400点、🟫茶diff)

問題

atcoder.jp

解法

最小値を計算量低く取り出すデータ管理構造、優先度付きキュー(priority queue)を使うことがポイント。
Pythonではheapqとして提供されている。

全体に対する玉の数の上下はoffset変数を作って管理。
玉を追加する時は、offset値を引いて追加する。

優先度付きキューの特徴

  • 最小値(最大値)を O(logN)O(log⁡N)で取り出す
  • 要素を O(logN)O(log⁡N) で挿入する
    ことが出来ます。 通常のリストだとそれぞれ O(N)O(N) ですので高速です。
    「リストの要素の挿入」と「最小値(最大値)を取り出す」ことを繰り返すような時に使います。

実装

参考

qiita.com

AtCoder Beginner Contest 212 [ C - Min Difference ]をPythonで解く(300点、⬜️灰色diff)

問題

atcoder.jp

解法

数列A、数列Bが2 x 105なので、A,Bの全要素に対して総当たりだと、2 x 105の2乗なので108越えでTLE確実。

なにか最適化する必要がある。
おそらく二分探索だろうと、検索したところ、そういえばbisectという便利ライブラリがPythonにはあることを思い出す。

数列A、Bをソート。
数列Aの各要素に近い値を数列Bからbisect_rightで二分探索。
bisect_rightの結果のIはB[I-1]とB[I]を使用。
計算量はO(N logM)。

実装