Kyo_s_s Homepage
AHC030 参加記
競技プログラミング
AHC

99位、パフォーマンスは2018でした。レート 1981 -> 2003(+22) でした。黄色になりました!

問題: https://atcoder.jp/contests/ahc030

GitHub: https://github.com/Kyo-s-s/AHC030

どんなことをしたのか書きます。

Warning

誤った解釈/主張をしている箇所があるかもしれません。 その場合はX(Twitter)などで教えていただければありがたいです。 XのID: @Kyo_s_s


ベイズ推定

Pi((x,y))P_i((x, y)) を 油田 ii が(左上から) (x,y)(x, y) だけずらした位置にある確率 とします。 これをもとにベイズ推定を行いました。

この定め方により、任意の ii に対して x,yPi((x,y))=1\sum_{x, y} P_i((x, y)) = 1 が成り立ちます。 また、初期値は入力生成方法より一様です。

クエリ結果を qq とします。このとき、ベイズの定理より、

Pi((x,y)q)=Pi(q(x,y))Pi((x,y))Pi(q)\displaystyle P_i((x, y) \mid q) = \frac{P_i(q \mid (x, y)) P_i((x, y))}{P_i(q)}

が成り立ちます。  また、 Pi((x,y))P_i((x, y)) を事前確率、 Pi((x,y)q)P_i((x, y) \mid q) を事後確率、 Pi(q(x,y))P_i(q \mid (x, y)) を尤度 と言います(あってるよね?)。

Note

P(BA)P(B \mid A) は、「事象 AA の元で事象 BB が起こる確率」 です。

事後確率 Pi((x,y)q)P_i((x, y) \mid q) は「クエリの結果として qq が返ってきた下で、油田 ii が 位置 (x,y)(x, y) に存在する確率」、 尤度 Pi(q(x,y))P_i(q \mid (x, y)) は「油田 ii が 位置(x,y)(x, y) にある下で、クエリの結果として qq が返ってくる確率」となります。

ベイズ推定では、クエリ毎に事後確率を求め、事前確率を更新していきます。つまり、クエリ qq を聞いたのち、

Pi((x,y))Pi(q(x,y))Pi((x,y))Pi(q)\displaystyle P_i((x, y)) \gets \frac{P_i(q \mid (x, y)) P_i((x, y))}{P_i(q)}

と更新します。

ここで、更新後も任意の ii に対して x,yPi((x,y))=1\sum_{x, y} P_i((x, y)) = 1 が成り立つことに注意すると、 Pi(q)P_i(q) は陽に求める必要はなく、一旦

Pi((x,y))Pi(q(x,y))Pi((x,y))\displaystyle P_i'((x, y)) \gets P_i(q \mid (x, y)) P_i((x, y))

と更新したのち、

Pi(x,y)Pi((x,y))x,yPi((x,y))\displaystyle P_i(x, y) \gets \frac{P_i((x, y))}{\sum_{x', y'} P_i'((x', y'))}

とすればよいです。

以降、掘る/占い クエリ qq に対して Pi(q(x,y))P_i(q \mid (x, y)) を求めることを考えます。

掘るクエリに対する更新

座標 (h,w)(h, w) を掘って、油田の量が vv であることが分かったとします。 この時のクエリを q=(h,w,v)q = (h, w, v) と表すこととすると、求めたい値は Pi(q=(h,w,v)(x,y))P_i(q = (h, w, v) \mid (x, y)) 、つまり「油田 ii が位置 (x,y)(x, y) にある下で、 (h,w)(h, w) の石油埋蔵量が vv である確率」です。

これは、油田 ii が位置 (x,y)(x, y) にあり、

と等しいです。 これらの値は油田 ii 以外についてのDPをすることで求めることができます。

Warning

そのままこの値を計算すると値が小さくなりすぎてしまい、アンダーフローが起きてしまう可能性があるかもしれません。 コンテスト中には気づかずそのまま実装してしまいました。

占いクエリに対する更新

座標の集合 S={(h1,w1),(h2,w2),,(hn,wn)}S = \{(h_1, w_1), (h_2, w_2), \ldots, (h_n, w_n)\} を占って、返ってきた値が vv であったとします。 この時のクエリを q=(S,v)q = (S, v) と表すこととすると、求めたい値は Pi(q=(S,v)(x,y))P_i(q = (S, v) \mid (x, y)) 、つまり「油田 ii が位置 (x,y)(x, y) にある下で、集合 SS を占って vv が返ってくる確率」です。

先に言ってしまうと、この値は厳密に求めずに適当にしてしまいました。 なので以降の文章は適当です。

normpdf(x,μ,σ)\mathrm{normpdf}(x, \mu, \sigma) を、 平均 μ\mu および標準偏差 σ\sigma の 確率密度関数に xx を代入した値 とします。

v(S)=0,1,v(S) = 0, 1, \ldots に対して、集合 SS を占ってクエリ結果 vv が返ってくる確率 というのが、

v0.5v+0.5normpdf(x,(Sv(S))ϵ+v(S)(1ϵ),Sϵ(1ϵ))dx \displaystyle \int_{v-0.5}^{v+0.5} \mathrm{normpdf}(x, (|S|-v(S))\epsilon + v(S)(1-\epsilon), |S|\epsilon(1-\epsilon)) dx

で求められます。これを p0,p1,p_0, p_1, \ldots とします。 (ここで、 v(S)v(S) は集合 SS の石油埋蔵量の総和です。読みづらくてすみません…) (x=0x = 0 のとき、元が負数で 00 にまとめられるケースもあるのでこの時は -\infty から 0.50.5 までの積分値となります) (ぼくは問題文が読めず、積分区間を vv から v+1v + 1 にしていました。バカ…)

Note

今回は数値積分を用いてこの値を求めたのですが、誤差関数を使うと良いらしいです(そのほうが高速?)。 Rustでは libm::erf が使えるらしいです。

油田 ii を位置 (x,y)(x, y) に置いたとき、少なくとも v(S)v(S) の値は、 集合 SS の座標の中で、油田 ii を位置 (x,y)(x, y) に置いた時に重なる個数以上になります。

重なる個数を ww と置き、Pi(q=(S,v)(x,y))P_i(q = (S, v) \mid (x, y))(k=wpk)/(k=0pk)(\sum_{k=w}^\infty p_k) / (\sum_{k = 0}^\infty p_k) であるとして更新を行いました(ここが適当ポイントです、本来は他の油田の情報も考える必要があると思います)。

戦略

まず、N×NN \times N の島を 4×44 \times 4 くらいずつに分割し、占いクエリを投げます。 これにより、油田がある可能性がある/ほとんどない といったことがある程度分かります。

その後、油田量の期待値が 0.60.6 に近い座標を掘り、確率を更新する… ということを繰り返しました。

この手法だと終盤に確率が非常に小さい箇所をたくさん掘ってしまいます。そのため、 期待値がある程度以下の座標については、掘らずにまとめて占いを行うようにしました。seed19の終盤でこの挙動をしています。

各油田について存在する位置がある程度以上特定できたとき、提出クエリを出すようにしました。

seed0 seed = 0, Score = 2800000

seed1 seed = 1, Score = 37982051

seed19 seed = 19, Score = 216280847

色はその座標の油田量の期待値です。11 以上は緑、00 は白として表示するようにしました。

占いで適当なことをしているため、基本は11マス掘り戦略をしました。

MM (油田の個数) が小さいときには各配置に対してベイズ推定をする方針が強く惨敗していたのですが、 MM が大きいときにはそこそこ強かったらしいです (M=18,19,20M=18, 19, 20 ではそれぞれ 17, 31, 35位でした。 https://siman-man.github.io/ahc_statistics/030/index_m.html 参照)。

入力ケース生成方法によると MM が小さいケースが圧倒的に多く、負けてしまったようです。

この方針の課題
占いの更新の仕方が適当すぎる

困っています。ちゃんとやる方針を考えたのですが、重そうな気がしています。

同じ形の油田が存在するとまずい

困ります。同じ油田の形だと全く同じ遷移をしてしまい、確率が 11 に収束しないためです。

今回は同じ形があったときには確率が高いものから採用するようにしましたが、終盤でこのことに気づいたため、だいぶ実装がごちゃごちゃになってしまいました。

感想

コンテスト中はこれでいいじゃん!と思っていたのですが、いざ参加記を書くと本当にこれ正しいの?と疑問に思う点や、 適当すぎない?という点がたくさん出てきてしまいました。むむむ…。

とはいえこれでHeuristic黄色になりました。うれしい! この記事とは別に、Heuristic入黄記事を書くかもしれません。

もっと強くなりたい~

© 2025 Kyo_s_s