inani_waonの日記

コンテスト覚書

トヨタ自動車 プログラミングコンテスト2022(AHC015)- Halloween Candy 参加記録

トヨタ自動車 プログラミングコンテスト2022(AHC015)に参加しました。

 

atcoder.jp

 

問題名はHalloween Candy。グリッド上でランダム湧きする飴をスライドさせる問題でした。

結果は提出807人中198位でした。

 

問題

10*10のグリッドに飴が1つずつ置かれるよ。

飴の種類(全3種類)は100回分事前に分かってるけど場所はランダムだよ。

飴が置かれる度に盤面全体を傾けてスライドさせて、巨大な連結成分を作ってね。

 

考察

うーんこれはIS-MCTS

偏らせれば良さそうなのは分かるけど、偏らせる方法が分からん。

 

実装

やりました。

高速化しようとSIMDに手を出したら遅くなったので、最低限やっただけになりました。

IS-MCTSはRustでMCTSを書くときついでにやろうかなと先送りにしていたのですが、短期コンで出てくるとは…。書いたことないどころか理解しきってもいないものを短期コン中に作り始めるのはクレイジーですが、なんとかなりました。

でもそもそも完全に理解していないので、本当に出来ているのか分かっていません。いずれ再調査しないと…。

 

他の人の解法を見て

1手先に湧く飴に合わせて動くのが良かったらしい。

シミュレータで言えば、Nターンで打ち切るのがいいとか、プレイアウトにルールベースを仕込むといいとかは、いつものモンテカルロ法のお話。この辺は問題の性質も考察しないと組み込みにくいですね…。

今回は考察は脳死でIS-MCTSの実装に注力したので順位は微妙でしたが、手を出すきっかけにはなったので良かったです。

Topcoder MM141 - TrafficController 参加記録

TopcoderのMarathonMatch141に参加しました。

 

www.topcoder.com

問題名はTrafficController。交差点の信号を制御する問題でした。

結果は58人中36位でした。(事前テスト順位も36位。)

 

問題

車が突っ込んでくるので信号を制御してね。

車は前の車が進まなくても交差点に進入するし、

交差点の中で信号が赤になると交差点から出ようとしないよ。

 

やったこと

試行錯誤

全ての信号を縦方向に解放して、一定ターンごとに向きを切り替える。5から始めて、3が一番良さそう。車の挙動がヤバいこと、デッドロックグリッドロックと言うらしい)が発生することは察していたので実際の挙動を確認する。

正の点を得る

ふと、縦横のいずれかを全開放してもう一方を封鎖すれば、理論値の半分強の得点が見込めることに気づく。流れで交差しない最大の道路数を求めたくなるが、探索すると最大を2^30未満には落とせない?なんとか過去の知見を活かせないものか。

縦横一方の使用道路を決めれば、もう一方はO(1)で求まる。縦横一方の使用道路を状態として、0.5秒焼くと相対62点*1が得られた。

真面目にやる

正の点を得る解法は投げ捨てて、状況に合わせて信号を変える。

直進可能になるよう信号を変える。

相対81点。

もうちょっと真面目にやる

交差点に進入する際に閉路チェックする。あまり上手く機能しなかったので、最終的には4辺からなる閉路のみチェックし、各辺の使用率が25%以上なら進入禁止とした。

またデッドロックが発生した場合は、影響する道の交差点へは進入禁止とした。*2

道路の優先順を付けたり車の発生率を考慮に入れたりしてみるが、結局のところ何も考えずにやるのが一番点が取れてうーん。

サボる

車列の長さなどを一切考慮していないのでまだまだやれそうな事はあるが、あまり気が乗らないのでお手軽な追い込みをする。

2つの解法ではっきり明暗が分かれがちなので、ケースによって解法を切り替える。L/N、L/R、(L^2)/Rなどを試し、手元1000ケースでは(L^2)/R>279が一番基準として良さそうだった。

相対84点。

 

感想

何も分からなかったけど、皆も何も分かっていなかったらしい。

やるならやりたい事はいくらかあったけど、今回は気が乗らず…。

*1:コンテスト終了時基準。

*2:進入禁止により前の交差点内が塞がって悪影響が出たりもする。難しい。

Topcoder MM140 - RobotPainter 参加記録

TopcoderのMarathonMatch140に参加しました。

 

www.topcoder.com

問題はRobotPainter。Forとか使って命令する系の問題でした。

結果は提出32人中10位でした。(事前テストも10位。)

 

問題

目標となるグリッド(トーラス環)を与えるので、ロボットに命令を与えて絵を完成させてね。

 

考察

これアプローチいっぱいありそう。ざっと考えただけでこれくらい出てくる。

・多くのセルを塗れるFor文を探す

・線の集合とみなしてTSP

・命令を木探索

・命令を全探索

一番上のはHTTFで高橋君メイドロボがやってたやつ。ただ今回は白マスに入ると大きなペナルティが付くのと、塗り残しが1,2マスあると結局線を塗りなおさないといけないので隙間だらけの塗り散らかしをするのは難しい。

線を塗りきることを考えると線を抽出して塗り順をTSPするのが良さそうな気がするが、「ここは別の線で塗るから1歩手前に止まっていいよ」とか「ここは1周できるから好きな場所に止まっていいよ」を上手く計算できる気がしない。

Forを考えるのも†全て†を考慮すると計算量が爆発するので、何か筋の良い方法はあるんだろうなぁと思いながらForはスルーして探索することに。

 

最終的にやったこと

N6~18はchokudaiサーチ。N19~30は貪欲法。

貪欲法

「残り塗り数 * 200 + コスト * 100」が主に最小化されるよう、「回転→F or B」を行う。無理なら浮かせてそのままの向きで移動したり、Jump。

時間いっぱい試行するが、2回目以降は評価関数の重みをランダムに決める。残り塗り数は11~199、コストは11~799。

申し訳程度に、以下のループを試行毎に10%の確率で1回だけ試す。*1

For 5~10

  F (N-1)

  J (任意)

End

chokudaiサーチ

ある程度の操作単位で区切って探索する。回転→移動が1セットとか。

無駄な動きは出来るだけ刈る。F 1→F 1とか、Up直後にDownとか、塗り済みの場所にJumpした後再Jumpとか。*2

状態遷移図を書いて刈るべき動きを見定め&定義したり、それとは別にifでチェックしてフラグを立てたり。

 

感想

独創的なアプローチが見つかったり各々でアプローチが違えば本当に楽しそうなコンテストだと思いました。結局最後まで良いアプローチが見つからず、若干の消化不良…。

最近は多態性で殴ることも増えてきたのですが、複雑なことをするならやっぱり新しい言語バージョンでやりたいなぁの気持ちがありますね。マラソンだと単一ファイル、実験前提なので書き殴りになりやすい、みたいなのがありどうしても汚くなるので、そのうえバージョンまで古いとなると、どうも先に繋がるコードを書けている気がしない…。

*1:塗る数の下限を設定し忘れていたので、改悪になっている可能性が高い。

*2:今回はハッシュ衝突が怖くてZobristHashを試してなかった。

RECRUIT 日本橋ハーフマラソン 2022夏(AHC013) - Server Room

RECRUIT 日本橋ハーフマラソン 2022夏(AHC013)に参加しました。

atcoder.jp問題はServer Room。移動と連結を共通の手数で行う問題でした。

結果は提出948人中74位でした。(暫定50件は56位)

システムテストだと1件平均5200点くらい。

 

問題

(問題文そのままの用語だと長くなるので、コンピュータの種類は色、コンピュータはサーバと言い換えます。)

グリッド上にあるサーバを動かしたり直線で繋いだりして、良い感じに連結成分を作ってね。サーバは2~5色あるけど、なるべく同じ色同士を繋いでね。

 

考察

とりあえず点数計算を読むと、同色サーバ100台の4950点クラスタを何個作れるかという問題に見える。

MM139、MM128に似ている。*1密なケースの移動にはAHC011も応用できそうかな。あとHTTFでやった最小全域木。とりあえず今回は、前提としてあまり頑張る気力が無いので安定した点数を取ることを目標にする。

とはいえNやKにより性質が変わり、複数の解法を使い分けることもありえそう。解法は1個か、多くて2個しか実装できないだろうなぁ。汎用性が高くて、4950点(に近い点数)のクラスタを作ることに特化した解法を作りたい。

 

候補はMM128と同様に目標盤面を作ってから動かすか、最小全域木…かなぁ。でも接続回数が移動回数の影響を受けるというのもあって、後者は具体的な方法が湧いてこない。作って思い通りに動く保証も無いし、汎用性の高さで言えば目標盤面を作る方法か。こちらはレイアウトによって何手動かす必要があるのか概算できるので方針も立てやすいし、サーバが直線状に並ぶので失敗してもリカバリーをかけやすい。とりあえず接続を考えずに動かすだけ動かして、移動を何回で打ち切るかを全探索しちゃえばなんとかならないかな。

 

次に目標盤面の形を決める。似た問題のMM128では、渦巻や、表面積を最大化したアメーバのような形が強かった。でも今回の問題では全マスにサーバがあるわけではなく、置かなくてもいい場所がある一方で、曲がる場所には必ずサーバを置かないといけない。なので疎な盤面に対しては角の設置のために過剰な移動を強いることになるかも?ということで、今回は曲がる回数を少なくする方針に決定。実装楽そうだしね。*2初動は多層渦巻、途中からは1色狙いは短冊の中央に串を刺した形、2色狙いは天井と床から棘を生やす形にした。

(使用位置を取捨選択したり調整したりができれば曲がる回数が多くても良いと思うが、良い感じに調整できる気がせず…)

 

左:1色の動作例 右:2色の動作例

 

実装

基本の動きは、以下の4つのフェーズ。

1. 目標盤面を作る

2. 目標盤面を目指して動かす

3. 交差点にサーバを動かす

4. サーバを接続してクラスタを作る

 

1. 目標盤面を作る

1色狙いと2色狙いをそれぞれ作る。縦縞を作って、1色なら真ん中、2色なら上下に横棒を刺す。横棒を一直線にしつつ間にも余計なサーバを入れないことで、「こことここが繋がらない」みたいな事故を避ける。5000点と1000点を半々で出すより4000点を安定させたい。

縦縞を横に何個ずらすかと使用色の組み合わせを全探索して、1色2色のそれぞれで初期盤面に近いものを採用する。

ちなみに使わない色は全部同じ扱い。使わない色のサーバを詰め込む場所はゴミ置き場と呼んでいた。

2. 目標盤面を目指して動かす

BFSで移動距離が近いものから貪欲に動かす。移動元と移動先のマッチングはサボる。

3. 交差点にサーバを動かす

貪欲に動かす。目的の色のサーバがあまりに遠いことがあるので、元々別の色のサーバがいる場合はそのままとし、ダイクストラでも別の色を持ってくる追加コストを距離5相当とした。

4. サーバを接続してクラスタを作る

まず横棒を接続したら、あとは大体貪欲にやる。直接接続できない場合は、1個まで他色のサーバを経由する。

 

ここまでが基本。

手数を節約したり、ちょうどいい解を探すために、いくつか追加のロジックがある。

 

移動回数を探索する

何回で移動を打ち切って接続に移るかを探索した。前半の移動はコスト1、後半はコスト3などの非効率なものになるので、コストが軽い移動が優先されることになる。

バグで接続の計算が重い時期があり、高速化のために移動回数は三分探索した。*3最序盤は盤面が整っていないので低スコア、最終盤は移動に手数を使いすぎるので低スコア、中央はちょうどいいので高スコアとして、概ね凸な関数になっていることを期待した。ただし完全に凸というわけではないので、候補が30個くらいに絞り込めたらあとは全探索する。提出スコアが0.1%低下した代わりに4倍速になったので、上手く機能してはいそう。

使わなかった位置をゴミ置き場に変換する

例えば左から一直線に[1-1-1-1-1 3 5 2 4]と1だけを繋いでいる場合、最後の1以降である3524は1がいるべきエリアに居座っていてもいい。実際の動作ではどかしてしまうので、最高スコアの解を求めた後に目標盤面を修正してどかさないようにして、再度計算する。この浮いた手数で点数上昇を狙う。

余計なサーバを取り除く

例えば[1-2-1]と直線で繋がっている場合、このまま繋ぐと2手かけて接続することになる。これを「2をどかして1と1を接続」に変換すると、2手のまま不純物を取り除いて得点を上げることができる。このどかした2が良い仕事をする可能性もあるため、処理としては2をどかして接続をやり直すとした。

目標盤面でなくても隣ならOK

使用したいサーバがゴミ置き場にいても、目標盤面にある同色のサーバと隣接していれば移動は必要ないとみなす。基本的にこの状態のサーバを目的地に動かすには2手以上かかるので、2手浮く計算になる。ちなみにもう1色の使用色の邪魔をする配置は駄目。

 

…という感じ。

最終的には、2色使用時は6000~7000点くらいが多いのかな。2色使用だと1手移動するやつを一部動かして終わりなので、4950 *2点は流石に遠そうでした。

 

やらなかったこと

他色サーバ位置を経由した移動

邪魔なサーバを隣にどかして通過すれば2手、そのサーバを戻すとしても3手で経由できる計算。多色だと欲しいサーバが閉じ込められていることもありこのロジックは欲しかった。実装時間切れ。

目標盤面の90度回転

サボり。あとは縦縞のレイアウトや高さを可変にするとかも。

位置のマッチング

速度が割とばらつくので、安全のため切った。N対Mのマッチングになりがちなので、こういうときは最小費用流が欲しくなる。

その他細かい調整

交差点に動かすときに他色サーバを置く追加コストを可変にして探索する、移動順や結合順をもっと検証する、などなど。

ローカルでベンチマーク

そこまでやりたくない気分のときもある。

 

使用したアルゴリズム

  • BFS、ダイクストラ(経路探索)
  • Dial's Algorithm(ダイクストラ高速化)
  • 三分探索(移動打ち切り数探索)
  • UnionFind(サーバ連結の管理)
  • ハンガリアン法(使っていた時期もありました)

 

他の人の感想を見て

なんで皆焼いてるの???

何を食べればこれを焼いて解ける問題だと思えるの???

 

感想

NとKで性質が変わる問題でしたが、私の解法だとそこまで方針に違いは出なかったですね。もっと良いやり方はあるにせよ、同じ方針でも破綻はしないよという意味で。そういう意味では、手法の選択は成功と言えそうです。最小全域木を弄るのは無理無理。

4950点以降は打ち込むほど楽しいタイプのコンテストに見え、やりたいことは8割終えられたので満足です。

*1:どっちもRegionalsなのは何の因果か。

*2:楽ではない。

*3:バグのせいだったので、実際は全探索で良さそう。

Topcoder MM139 - PipeConnector 参加記録

TopcoderのMarathonMatch139に参加しました。

www.topcoder.com

問題はPipeConnector。組み合わせと経路最適化の問題でした。

結果は72人中29位でした。

 

問題

グリッド上に色付きのノードがあり、1~9の数値が付いているよ。

同色のノード2つをパイプで繋ぐと数値の積の点が得られるよ。

複数のパイプが重なるとペナルティが付くよ。

 

参加記録の前に

めっちゃ長い苦情です。口うるさいので気に入らない方もいるかと思いますが、ご了承ください。

今回はC++のサンプルコードで誤ってtester解が公開されるというトラブルがあり、N回に一度の酷い回でした。サンプルコードを告知なしに入れ替えたことでまた別のトラブルになっていましたね…。*1

もちろんこのトラブル自体は開催者が起こしたもので開催者が悪いのですが、*2参加者側も混乱を起こしてアウト寄りの行動が目立っていたように感じました。

まずこのコンテスト参加の前提として、他者と協力してはならない、公平である必要がある、というものがあります。そして、『コンテストの内容に関係があり、他者の解法に影響が出る発言』をするのは他者との協力にあたり、また『一部の人間が知る』>『全員が知る』で不公平になると考えています。

ただし他者との協力とはみなされない例外があって、それは問題を解決するための質問や報告です。そういう報告にも、全員が見ることが出来るフォーラム、もしくは全員が見ることができない管理者へのメールを利用しましょう。twitterで言及するのは見る人間が限られるうえに問題の解決に直接繋がらないため、目的・影響の両方の意味で良くないです。

(具体的には、サンプルコードに異常があることを明示・暗示する発言は報告としてフォーラムか管理者メールへ書くべき、サンプルコードの点数への言及も議論目的なら同様、議論目的でないならそもそも言うべきではないと思いました)

また、フォーラムに出た内容は全員が知る内容なので日本語訳して転載・引用すること自体は大きく問題視していませんが、それに対する言及・質問でフォーラムに無い内容への言及に繋がっているなぁというのも感じました。これは翻訳情報があるほど言及が増えますし、フォーラムを直接読まないけど日本語情報が欲しいという人がいるほど翻訳情報の需要が増えて発信が活発化してしまうので、どんどん言及が増える負のループになってしまうところがあります。(そもそも、フォーラムを読まずにフォーラムに無い情報への言及を避けて話すのは不可能なので、翻訳情報を見てあれこれ話すという流れはおかしいのです)

なので、コンテストを壊さないためにも頑張ってフォーラムを読みましょう。必要なのは翻訳サービスを立ち上げる頑張りだけです。投稿するのは…こちらも翻訳サービスがあるとはいえ、私もまだ気後れしてしまいます。それでも『1文を短くする』『単一の解釈しか出来ない書き方をする』といったテクニックを使うことで、いざという時には最低限の発信は出来るかなぁ*3と考えています。

駄文おしまい。

 

考察&実装

とりあえず、大きい値同士を繋げば実スコアが大きくなるし、近い頂点同士を繋げば交差を減らせる気がする。頂点の組み合わせを全列挙&評価して、スコアの降順に貪欲に辺を張らせるところからスタート。1000件で平均966点。

(え、交差って平行に重ねるのもアリなんですか…?)

既にある辺を考慮して遠回りとか出来ると強そう。辺を削除して再度経路探索すればいいかな。これを10回繰り返して、更に空いた場所に辺を張りなおしてというのを3回繰り返してみる。平均1034点。

(3日コンだから短期寄りに、単純な解法の方が良いかなぁ)

雑に焼いてみる。近傍は辺の追加、削除、辺1本の経路再決定。1秒焼いて1067点。

辺2本が交差しているので解消したいなとか、{7, 1}と{3, 3}を{7, 3}と{3, 1}に変更したいなーみたいなのが結構ある。これを追加と削除だけで遷移させるのは厳しいので、2-opt風に頂点を交換する近傍を追加する。

これはかなり苦労して、遠くの辺同士を交換しても意味が薄いので近場に限定するとか、近場ってどう判定するのというので辺*辺の枝刈り探索で前計算したりした。前計算に1秒以上かかることもあるので、1秒測定が信用できなくなってくる。1082点。

積が6以下のペアって無視しても良くない?という悪魔のささやきが来る。確かに良さそうなんだけど、結果的には{7, 1}と{3, 3}を{7, 3}と{3, 1}に変更したいときに{3, 1}を構築出来ないので変更できないというのでスコアが悪化して没。

ここから手元テストを100件で回すことにする。この時点で100件で995点。

あとは温度調整と高速化がメイン。それ以外だと、辺追加の近傍は頂点が空いてないと出来なかったのを、既存のを壊して追加するというのを入れた。

(遠い状態に遷移する力が足りないし、近くへの遷移もたまに取りこぼしがあるなぁ)

焼き鈍しは今まで偽物の焼き鈍し*4を使っていたので、本物(?)の焼き鈍し*5を使ってみた。スコアの絶対量に対して変化が小さすぎると反映すべきかの判定がきついよなーと思っていたんだけど、今回は辺1本あたり1~81点の加算と交差ペナルティの減算ということで、スコア差分だけ見て計算するのがやりやすかった。性能はちょっと上がったかな。差によって採択率を大きく変えて、高温から始めるのが良いっぽい?

高速化パートでは、優先度付きキューが実行時間の半分を占めていたのでDial's Algorithmに変更したら該当箇所が3倍速以上になったとか、StateのCloneが重い*6のでRollBack可能な方式にしたら20倍速以上になったとか。優先度付きキュー、地味にボトルネックになりやすいんだよね…。そしてStateは高速Cloneに対応した自作構造が強いがち。

最終的には手元100件で1068点。

 

感想

今回はコンテスト自体が大荒れだったので色々あまり参考にならず。

様々な機能を乗せるうえで、どういう構造にしてどういうアルゴを使えば上手く出来るかみたいな要素はありましたね。チューニングを始めると開発+アルゴ感が強かった。

速度も大事そうな問題でC++サンプルのリークだったのでC#使いの私は悲惨なことになるかなと思ったんですが、青の人には抜かれてないくらいで済んだので致命傷一歩手前でした。*7

*1:個人的には、一部の人間がアドバンテージを得てしまった以上は全員に共有するのが筋だろうと思います。

*2:前者は完全にミス、後者は方針の違いということでやや中立寄り。

*3:かなぁ?

*4:更新に失敗すると割とすぐbest状態に戻す

*5:基本best状態には戻さない

*6:DictionaryのCloneが重かった

*7:致命傷で済んだ。

Topcoder MM138 - DiceRoller 参加記録

TopcoderのMarathonMatch138に参加しました。

www.topcoder.com

問題はDiceRoller。変則最長路問題でした。

結果は提出74人中、暫定23位でした。(システスはまだ)

 

問題

グリッドの各セルに数字が書いてるよ。たまに負数もあるよ。

サイコロを転がして(接地したまま4方向に回転させて)移動して、

底面とセルの値の絶対値が等しければセルの値の得点を得るよ。

始点と終点が隣接してたらループボーナスが出るよ。

あ、ダイス目も自分で決めてね。

 

考察

とりあえず、これは変則最長路問題。

比例までは行かずとも、まずは経路の長さがスコアに直結しそう。

とはいえ、負数は踏む必要は無いし、1のセルとかも多分そんなに要らない。

 

最長路を引きつつ、同じ値を同じ面で踏めるようにコントロール…きつくない?

経路を決める時点でダイス目が決まっていないと、評価が難しい。

ダイス目を決める時点で経路が決まっていないと、ダイス目を決めようがない。

ダイス候補を複数持って探索するという方法もあるけれど…。

とにかく探索はめちゃ難。

ループボーナスのために元の場所に戻ってくるのも大変そうだし。

 

じゃあ焼けばいいのか?というと、

輪を保つことは出来そうなものの、目の管理をどうするのかという問題が。

サイコロの向きってどう管理すれば…あ、24パターンしか無いんですか。

でも差分計算はきつそうだなあ。

近傍は破壊して再構築といういつものAHCメソッドが通用しそう。

 

うーん、基本焼きなましで、探索もちょっと挑戦してみる感じかな。

探索は明らかに難易度高そうなんだけど、

実は出来ますというパターンが最近多くて微妙な気持ちになっているし…。

 

実装

サンプルは渦巻らしい。ループしないものの、経路としては最長。

ダイスのシミュレーションはいずれ必要になるし、まずこの渦巻に対して最善のダイス目を求めてみる。42点。*1サンプルが16点で3倍くらい。やっぱり得点の上限は低いっぽい。

これ以上は、面ごとに踏む値を偏らせるか、ループでしか点を上げられない。

 

ループと言ってもどういう形にすればいいのか。渦巻を二重にしてとりあえず完成。が、これ駄目じゃん。真ん中以外は経路の一部を破壊しても一本道しか空かないから、ほぼ再構築結果が元と同じになる。再構築で変化しやすいよう、長さ20前後?の経路を壊したときのスペースが4x5とか、そんな感じで再構築の余地があると良いかな。でもメアンドロス模様みたいにしちゃうと隣のブロックとの境界が強固…。ん-、大きいジグザグ移動の中で更に小さくジグザグ移動する感じ?

 

それはそれとして、chokudaiサーチも試してみる。

駄目でした。(2日間ロス)

 

焼き鈍しに戻る。2日前に考えたパターンを作って64点。更にパターンを見直したり高速化したりで82点。とりあえず爆死回避。

Nの幅が広くて、Excelテンプレ職人になれなくて辛い…。

 

見た感じ、再構築しようにも空きマスが無くて変化できないというケースが多そう。

kick的なものが無いから局所解にはまってそう。良い近傍に移れる確率がそもそも低いし。

隣り合ったセル間の経路を破壊して空き地にするという近傍を作ってみる。空き地にした後に有効な遷移が続くことがほぼ無く、邪魔をしているだけっぽい。どうすれば…。

 

では下図のように、初期解の時点で隙間を空けるのはどう?

N=16だけお試しすると、5~10%の改善が出てテンションが上がる。

他のNにも展開すると、何故かスコアが下がる。なんで?

どうやら、ルート再構築のDFSがなかなか終わらずに焼き回数が極端に落ちてしまったらしい。そっちと絡んでくるのか。難しい…。

 

それはそれとして、高速化に着手してみる。差分計算すれば毎回ルート構築する必要も無いはず!

→差分計算するための膨大なメモリをどうやって高速に初期化するんだい?(1日間ロス)

 

普段1000msで試しているんだけど、たまに9000msでも試す。すると、どちらでもスコアが変わらないケースが結構ある。局所解で8秒停止は不味い。4秒更新無しでリトライするようにする。ついでに、そのときは初期解を90度回転させておく。

 

空きマスが無い問題、kick的な空間空けとして、一部破壊して最短経路で結ぶというものを試してみる。僅差だけど改善はした、かな。お守りとして入れておく。

 

あとは高速化や調整をちまちま。

84点でフィニッシュ。

 

他の人の解法を見て

・DP

なるほどね(評価出来るかが怪しい)

・再構築時、ダイスの向きが同じになる経路に限定する

それは確かにそう。

以降のルートで目が変わってしまうと直前までの解はほとんど役に立たないし…。

ただ、最初期はそこを気にしないほうがいい可能性もあり、微妙に厄介そう。

・空きマス数を評価

確かにそう。変更の余地が無くて苦しんでいたので、空きマスの必要性は分かっていたはず…。(でも多すぎるとDFSが爆発して死ぬ)

 

感想

方針空振りを恐れすぎて、自分の走りが出来なくなっているかも。

(競プロ基準で)実装が速い方ではないので、他の方針のお試しに時間を削って、言語も最高速ではないC#、アルゴ力もそこそこレベルとなれば、まあ勝てる道理も無く…。

今は修行期間として色々試してみるにしても、いずれは芯のある走りに戻したいですね…。

*1:得点は終了時のもの。

AHC012 - AtCoder 10th Anniversary 参加記録

AtCoder Heuristic Contest 012に参加しました。

atcoder.jp

問題はAtCoder 10th Anniversary。

円形のケーキをカットして、苺を上手く配分する問題です。

結果は得点率91.5%で、1659人(うち提出710人)中90位でした。

 

問題

円形のケーキ上に、苺がたくさんあるよ。

100回まで直線でカットして、苺が1~10個の領域をそれぞれ指定した数だけ作ってね。

要求数に対して作れた割合(苺の個数毎に、上限100%)がそのまま得点になるよ。

 

考察

んん、三角関数系か?4時間ではやりたくないなぁ。

 

まずは生成ルールを読んで、必要なブロックの数を概算する。

中心を通るように角度を変えて100回切るとおよそ200ブロック。全然足りない。

縦に50回、横に50回切るとおよそ2500ブロック。ケーキは円なので、角にあたる部分が無い分それよりは少ないが、めっちゃ余る。まぁ多すぎる分には切る回数を減らせばいいよね。

切り方はこれで行こう。斜めカットは…やるならかなり本腰入れないとなぁ。時間ありそうならで。

 

苺の数は必要最低限しか無い。満点が取れない場合、苺をたくさん(9~10個)要求する人を切り捨てた方が得点は改善しやすい。

 

ブロックの状態は苺の個数が同じなら同一なので、垂直/水平に切るなら苺2つの間の何処で切ってもいい。つまり代表位置を1つ決めれば、カット位置は垂直/水平それぞれでN-1箇所程度になる。(X位置、Y位置の重複があれば減る。)

 

水平に10分割した後、垂直に切る位置をビームサーチ、とかどうだろう。10本の帯に見立てて、いずれかの帯が苺11個以上になる切り方は不許可、とすればまあまあ枝刈り出来ない?

評価は要求苺数ごとに、[不足ブロック数^2]の総和かな。ただそうすると終盤に苺10個みたいな切り方をするのが難しい…。外周に小さくカットされる部分が出来るので、どうしても1,2辺りが余分に出来そう。中央から両端に向かって同時探索して、大きいものを優先的に作るよう探索するとか?序盤は苺10個を重視したいくせに、全体としては苺10個は軽視したい。この実スコアと相反する探索をするのが気持ち悪い。…焼くべき?とりあえずシミュレータ書きながら焼きなマシーン*1の様子を見るか。

 

時系列やったこと

~1:00

考察&入力の受け取り。

~1:07

入出力のテストがてら、20x40に固定幅で分割してみる。

70M点で420位相当。

~2:10

とりあえず焼きなまし完成。

初期解は20x40で、近傍は垂直分割(40個ある側)の追加/削除/移動。

89.9Mで108位相当。

~2:14

移動が何処にでも飛んでいく仕様だったので、近場に制限してみる。

89.0M。なんで?

あと、差分計算できるところを毎回計算して遅いので、試しに時間を10倍にしてみる。得点ほぼ変わらず。これなら高速化より、初期解と近傍を見直すべきか。

~3:15

なんとなく細長い方が得点が高そう?あと多点にしたいので、水平分割を10と11で半分ずつ回してみる。(10x40)

90.4Mで100位相当。

~3:57

初期解を10x60にする。あとパラメータ調整。

うーん、初期解から劇的に変わってる気がせず、kick的なものが無いと苦しそうな気がする。

91.5Mで90位。

 

他の人の解法を見て

斜めに線を引いている人もいるけど、格子状で大体戦えそうだった。高速化、初期解、近傍辺りを詰めてればもっと高得点取れてたかな。多点の話は全然出てない。

あとは縦横を両方動かしてる人もいたけど、速度が多少犠牲になるのでどっちが良いのかくらい。多分遅くても両方動かせた方がいいかな。

 

感想

高速化などの手を回しきれない部分もあったけど、出せる力が素直に出しきれたので短期コンの割には良い成績でした。

苺を切らずに垂直(水平)に限りなく近く切る方法があったらしく、そういう非本質な裏技で点稼ぎできるのはちょっと…と思うも、そこまで点に響くものではなかったかな。

問題のところに貼った画像のように、ユーザ歴10年の古参ユーザにだけケーキを届けることが出来ていない。すまない…。

*1:焼きなましに定評があるtakumi152さん。