Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
AHC023参加記(暫定版・手書き図多め)
Search
Shun_PI
September 13, 2023
960
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
AHC023参加記(暫定版・手書き図多め)
Shun_PI
September 13, 2023
More Decks by Shun_PI
See All by Shun_PI
AHC061解説
shun_pi
0
840
AHC045_解説
shun_pi
0
990
短期AHCで勝つためのテクニック
shun_pi
6
4.9k
Featured
See All Featured
The untapped power of vector embeddings
frankvandijk
2
1.9k
Side Projects
sachag
455
43k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
280
Optimising Largest Contentful Paint
csswizardry
37
3.9k
Documentation Writing (for coders)
carmenintech
77
5.5k
Unsuck your backbone
ammeep
672
58k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
A Tale of Four Properties
chriscoyier
163
24k
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
Code Reviewing Like a Champion
maltzj
528
40k
So, you think you're a good person
axbom
PRO
2
2.1k
Transcript
AHC023 4位解法+参加記 Shun_PI(@Shun___PI) 1
前置き • AHC023で4位になり、AHC赤色になりました • 解法そのものよりも、 考察過程を中心に説明していきます • 参加していない人も読むことを想定し、 できる限り問題の内容や性質についても 説明していきます
• 問題文自体は短いものの性質を理解するのは大変な部類の 問題なので、できる限り説明を頑張りますがついていけなく なってしまったらごめんなさい • 私のコンテスト中の実際の提出内容に沿いつつ、 「貪欲解(41.6M)」「焼きなまし解(44.5M)」 の2種類を時系列に沿って説明していきます 2
問題概要 • 20×20の土地があり、一部は壁で区切られている • K(約5000)個の作物があり、それぞれここまでに植えて いなければならないターンSと収穫しなければならないターンDが 決まっている • 作物を植える→収穫を100ターン繰り返す •
ここで、作物を植える・収穫するには、 そのマスから出入り口までが空きマスで 繋がっている必要がある • ただし、あるターンにおいて植える・収穫する 順番は自由に変えられる • 言い換えると、その時に植えた・収穫する予定のマスに 関しては跨ぐことができる • 収穫するターンDは必ず守らないといけないが、 植えるターンはSかそれより前ならいつでもいい • ただし、ターンSより前に植えてもターンSまでの間は 作物が植えてあっても土地の利用率にカウントされない • 土地の利用率を最大化したい • 利用率は、現在ターンよりターンSが前である作物の植わっている期間に相当 ここが 出入り口 3
問題の性質の確認 • 収穫するターンDは必ず守らないといけないので、 Dが大きい作物より奥にDが小さい作物を植えることはできない • 言い換えると、収穫する際はDが小さいものが手前、 Dが大きいものが奥になるように植えればよい • 植えるターンについては、Sより早ければいつでもよいので、 植えられるときに奥から手前に向かって植えてしまえばよい
4
提出1. 手前から奥に収穫するだけ • 開始1時間半の提出(実際はなんかWAになってた) • 489位(21.7M、緑パフォ相当) • 手前から奥に向かって Dの昇順(D同じならSの昇順)になるように 全てのマスに植える
• 出入り口からのBFSで計算した距離順に植えるのが楽 • 全て収穫が終わったら再び全てのマスに植える • この戦略は実装が簡単な割にはスコアがでるので、 多くの人が最初に試したと思われる • しかし、全ての作物を収穫しないと次の作物を 植えられないにもかかわらず周期が長すぎて、 明らかに無駄な空きマスが多く発生している 5
通路+グループ方針 • 周期が大きすぎることによる無駄な 空きマスの数を減らすことを考えたい • 周期が大きいのは、1周期で使用する作物のDの 幅が広くなってしまうから • あるDを持つ作物は20~30個くらいなので、400マス全てを 埋めると約15ターン分になり、前に収穫したマスは
14ターンくらい無駄になってしまう • 常に空けておき自由に動ける通路と それに接する小さなたくさんのグループを決める • 十分グループの大きさを小さくすれば常に同じS/Dの 作物を割り当てることでグループ内の 利用率を大きく上げられる • 各グループが独立に管理できるのも良さそう • 現実の農業のアナロジーもあり思いつきやすく、 かなり多くの人がこの方針を使っていた ・小さいグループの内部で作物を 割り付けることで利用率アップ ・常にどのグループにも到達できるので グループが独立に扱えてうれしい 6
通路+グループ方針の実装 • 作物の割り当ては提出1.と同じようにできそうだが、 通路とグループを決める部分を実装しないといけない • これは、BFSを駆使することで実現できる • 通路を木として作るには、 • 始点のみを通路とした状態からスタート
• 以下を何回かループ • 通路マスの中からランダムに始点を選ぶ • 始点からBFSする • BFSで到達したマスからランダムに終点を選ぶ • BFSの経路復元により最短パスを決定 • 最短パス上を通路に含める • グループを作るには、 • 通路マスに隣り合っている全ての非通路マスを 「グループの根」とする • 全てのグループの根から多点BFSをして、到達できた領域を それぞれのグループの領域とする 7 全ての始点が 「グループの根」
提出2. 木の通路を乱択 • 開始9時間の提出 • 6時間くらい外にいたので実働3時間 • 260位くらい(36.0M、水パフォ相当) • 通路木を前スライドの方法でランダムに決める
• グループを前スライドの方法で決める • 作物をDの昇順(D同じならSの昇順)にソートして おいて、空いているグループがあったら手前から 順に割り当てられる作物を割り当てていくだけ • 提出1.と比較して空きマスが長時間長い領域を 占める頻度がかなり減っている 8
グループ内利用率の改善 • 今はただソートした作物を順に割り当てているだけなので、 利用率が低い • 例えばD=10だけでグループを埋められるのに、D=9が残っていたから それも植えてしまうようなイメージ • 現在ターンsからあるターンdまでの計画を立て、 その利用率の大きさを評価指標とすることを考える
• 計画の最終ターンdを固定するのがポイント • 計画を立てるべきターンよりも作物の Sを遅らせる・Dを早めることはできるが、 その分計画の利用率が小さくなる • できる限り早いS・遅いDを割り当てるのが得 • よって、 (Sを何ターン遅らせるか)+(Dを何ターン早めるか) の和が小さい順に貪欲に作物を割り当てるのが最適 • これは右図のような順序で作物を貪欲に割り当てるということ • これを計画の最終ターンdに関して全探索し、 一番利用率の高い計画を採用すればよい 9
提出3. 利用率を評価値とした割り当て • 開始11時間の提出 • 6時間くらい外にいたので実働5時間 • 150位相当くらい(38.7M、青パフォ相当) • 通路木を前スライドの方法でランダムに決める
• グループを前スライドの方法で決める • 空いているグループに関して、作物計画を考える • 計画を決める最後のターンdを全探索すると 前スライドの方法で最適な割り当てと 利用率が求まる • 提出2.と比較すると、利用率(緑の濃さ)の変化 よりむしろ通路マスの数が大きく減っている • 大きいグループへの割り当てでも利用率が下がりにくく なり、通路マスが減るという形の恩恵 10
その他の貪欲の改善(未執筆) • グループの大きさに対して推定スコアを定めることで、 できる限りグループサイズが均一化(最大値を小さく) するように通路とグループ領域を焼く • 二段植え(長さAの作物の手前に長さBと長さCの2つの作物を 植えるような計画を探索する) • 多段植え(=直線DP?)
• 通路にも植えられそうなら植える 11
提出4. 通路+グループ方針の限界 • 終了1日半前、焼きなましを思いつく直前の乱択貪欲 (25位相当くらい、41.6M) • 通路+グループ方針だと41M~42Mあたりで 限界が来る • 通路を最初に固定してしまっているため、それ以外を
いくら頑張っても通路分の損失が大きい • 実際最終順位で20位辺りに赤い人とか AA社の人たちの壁があり、 このあたりが通路固定方針での限界に 近いのではと考えられる • DPで43M出しているすごい人もいる • 残り2日の時点で上位は44Mを出しており、 この時点で上位を狙うには何らかの新しい方針を 考えないとまずいと思っていた 12
上位解法のビジュアライザをイメージする • 上位の点数が明らかに異常なので、 仮に44Mの解法がある場合ビジュアライザは どんな感じか?を想像してみた • この時点で私の解法は41.5Mくらいなので、 2.5M=5%=50000点 の差がある •
これは通路マス20マス分を丸々無くすことに等しい • (20マス÷400マス=5% なので) • ってことは右図のような感じ!? • そもそも通路を減らす分他のマスの利用率は 今の解法より低めになるはずなのだから、 通路をもっと減らさないと5%の差は埋まらない • 従って、おそらく上位解法に通路は存在しない! • ちなみに、こういう大方針に関する考察をする際は 制約の小さい盤面を考えた方がいいと思っている • 壁の多いseed0より壁の少ないseed2とかseed3を使って 考えた方が良い • 私はseed3どころか壁の一切ない盤面を想定していました 13
上位解法のビジュアライザをイメージする • 通路が存在しない=常に一面緑色の状態を想像し、 毎ターン収穫する作物を考えてみると、 以下のような考察が生えてくる 1. あるターンDに収穫する作物は、出入り口を含む 連結領域になっている必要がある • 当然遠くのマスにも届かないといけないので、連結領域は
細長い感じになっていそう 2. 常に一面緑色になっていないといけないのだから、 作物を収穫した場所は次のターンに即座に 何らかの作物を植えないといけない 3. あるターンDに近いターンにおいては、収穫領域 ができる限り被ってはいけない • 特に長さ1の作物が無い制約のため、ターンDとD+1で収穫 領域が被ると大きな損になる • ここまで考察すると、上位のビジュアライザは毎回 違うルートの収穫領域(雷・放電現象っぽい感じ) になっている脳内イメージが見えてくる 14
さらに考察を重ねる • 前スライド考察3から、ターン毎の収穫領域の 被りを少なくすることが重要なのだから、 特定ターンの領域のみを最適化するのではなく、 全ターンの領域を同時に最適化する必要がある • あるマスについて各ターンの領域に 含まれる・含まれないが分かっているときに、 そのマスの作物計画が決定できるか考えてみる
• これは「そのマスで作物を収穫or空けておくターン」が 決まっていることを意味する • 前スライド考察2より、作物を植えるターンは 「そのマスで作物を収穫or空けておくターン」の直後である • 従って、 「そのマスで作物を収穫or空けておくターン」で 時間を区切って区間の集まりとして考えると、 各区間に「作物をその区間の最初に植えて最後に収穫」or 「何も植えない」のいずれかを割り当てることで作物計画が定まる • まとめると、全ターンの領域を決めれば 全てのマスが時間軸に沿った区間に分けられ、 各区間に高々1つの作物を割り当てることができる ことが分かった • ここまで考察した時点で上位は全ターンの領域を 焼いているだろうという確信が8割くらい 3, 8, 9, 15ターン目の収穫領域に 含まれるマスの場合 黄緑は実際に作物が植わっている期間 濃緑は作物のS~Dの期間(スコアに寄与する期間) Dは必ず区間の右端に無いといけないが、 Sは必ずしも区間の左端でなくてよい 15
焼きなましの近傍操作を考察 • 全ターンの収穫領域を持って焼くのだから、 近傍は「あるターンの領域を(連結を保ったまま) 1マス増やす・1マス減らす」となりそう • この操作がどのように行えるか考える • あるマスに領域を増やすことは、そのマスの区間の1つに 区切りを入れることに等しい
• 元々植わっていた作物を削除し、 区切った後の2つの区間に作物を割り当てるようにすれば、 状態やスコアを差分計算できそう • あるマスの領域を減らすことは、そのマスの区間の 区切りの1つを削除することに等しい • 区切りの両隣の区間に元々植わっていた作物を削除し、 区切りを消した後の1つの区間に作物を割り当てるようにすれば、 状態やスコアを差分計算できそう • (この差分計算だと作物の割り当てが全体最適と いう保証はできないが、ともかく)高速に近傍が 差分計算できそうなので かなり強い焼きなましができそう • ここまで考察した段階でかなり勝ちを確信しつつ 実装に入る 16
提出4. 焼きなましの実装 • 15位相当くらい(42.6M) • 直前の41.6M貪欲を初期解として、とりあえず 焼きなましを実装した最初の提出 • 厳密には乱択ができなくなったのでもうちょっと貪欲の スコアは低い
• 手元で25万ループくらい • まだ焼きなましが遅いので通路がかなり残っている ことが分かる(それでもスコアはかなり上がる) • ここからは焼きなましの高速化や効率化の改善を していくだけでスコアが爆発的に上がるようになり、 完全に別ゲーが始まった • ここからやっていることは問題固有のテクというより 汎用的な焼きなましのテクが多い • ので、ここからの解説はあまり役に立たないかも 17
提出5. 関節点高速化 • 10位相当くらい(43.4M) • 連結を維持したまま削除可能かどうかの判定に LowLinkを使った関節点判定をしていたが 遅くボトルネックだった • 自分の周囲3*3領域のみを用いた簡易関節点判定に
より高速化 • 割とよく出てくるテク?(AHC019で初めて知りました…) • 3*3領域内で自分とつながっているマス(赤色)が、 自分を削除後も連結なら削除してよい • 手元で175万ループくらい • 通路がかなりぼやけた感じになってきた 18
提出6. いろいろ改善 • 最終日前夜時点、7位相当くらい(44.0M) • なんか乱数にrand()を使っていて遅かったので mt19937にした • 前スライドで述べたようにこの焼きなましにおいて 作物割り当ては全体最適でない
• そこで、数千ループに1回全てのマスの全ての区間を 見直し、作物の再割り当てをすることで改善 • 手元で150万ループくらい • 今測ったら提出5.より回ってないんですが、あれ? 19
提出7. 最終提出 • 4位(44.5M) • 最終日の改善で最も効いたのは、 「領域の追加」操作の点数を高め、 「領域の削除」操作の点数を低くすること • こうすると、領域が常に最適な形よりちょっと大きな状態を
維持するので、関節点が減り削除操作がより活発に行われる • 乱数はmt19937でも重いので事前に1000万個作って それを使いまわした • 手元で500万ループくらい • 最終日は時間に追われ続けていたため何してたか あまりはっきり覚えていない… • 上位3人との差(敗因)としては、以下が考えられる • 関節点でも消せるようにすることで直線通路を曲げられる (消した場合それを迂回するようにマスを追加すればよい) • 単純に高速化が足りなかった(1位は1500万ループらしい) • そもそも焼きなましに気づいた時点で残り1日半で あまり時間が無かった 20
まとめ • 焼きなましを思いついた瞬間を私のツイートでは 「天啓来た」と表現していたが、 実際は天啓というよりは、順位表の上位のスコアを起点に 論理の積み上げができたからこそ思いつけている、 ということが分かっていただけたのではと思う • アイデアを生み出すためにどのような過程を 経ているのか?を重点的に説明したつもりなので、
今後の参考にしていただければ • こういう思考はAHCだけでなくKaggleや業務でも役立つはず 21