AHC014 参加記

:::note 注意書き 本記事はAHC014という、AtCoderで開催された株式会社estieさんによるヒューリスティックコンテストの参加記、および、解説記事です。 :::

先日開催されたAHC014というヒューリスティックコンテストに出場し、24位になりました。 本記事では、私の解法の概略と問題の性質について少し書かせて頂きます。

今回の問題において、上位勢はその殆どが焼き鈍しというメタヒューリスティックを用いていましたが、私はそれとは別のビームサーチという手法を用いました。

上記リンクにある解説放送における40:35あたりからの、writerであるwataさんの発言によれば、 「(ビームサーチなどの手法は)今回の問題はあまり向いていなさそう」 「少なくとも生のスコアを使ってビームサーチする限りうまく行かない」 などとのことでした。

結論から述べれば全く以って仰る通りなのですが、しかし、実はビームサーチという方針で、かつほぼ生のスコアを使ったとしても、24位くらいにはなれる(そして、恐らくその辺りがほぼ限界かも知れない)ということをお伝えしたく、本記事を執筆しています。

なお、自分より上位の方のコードをざっと見た限り、既に解説記事を書いていらっしゃるpenguin46さんを除いて、「beam」という単語はソースコードに出ていなかったので(かつpenguin46さんも焼き鈍し解のようなので)、恐らく私の解が提出者の中では一番良いビームサーチをメインとした解のようです。

くどい説明もあるかと思われますが、皆様にとって以下の解説が何らかの助けになれば幸いです。

問題

解法

具体的な解法を述べる前に、段階的に前提となる事項を整理していきます。

問題の性質に関する前提

前提A_1 一辺の長さが1の正方形をメインに考える

これは解説放送でも触れられていましたが、基本的には小さい矩形領域を使用した方がよいです。大きい長方形などを使ってしまうと、使用できる辺の本数が減ってしまい、結果として新たに置ける点の個数も減るので無駄が生じてしまいます。

この記事における「長さ」の定義を、感覚的には$max(abs(dx),abs(dy))$で定めるとしてやると、長さが1の正方形は当然以下の二つのみです。 (なお、私の方針では、基本的に点よりも線の方が重要なので、新たに追加された点が正方形の4頂点のいずれであるかなどは、今は考えないでもらって大丈夫です)

基本正方形

これらをメインに以下考えていきます。

前提A_2 長さ1の正方形はいくつかの種類に分類して考えることが出来る

先の前提A_1で考えた正方形について、(あくまで独自の用語ですが)縦横格子と斜め格子の二つに分類して考えることが出来る、つまり、傾いていない正方形と傾いている正方形でそれぞれ分類して考えることが可能で、どの辺を使用するかということはそれぞれ独立に決めて良い、ということは多くの方に直感的に理解して頂けることかと思います。

レイヤー分離.png

ところで、この縦横格子ですが、辺を重複しないような最も綺麗な(最密充填な)敷き詰め方は、偶奇性(以下パリティと書きます)に注目すると以下の2パターン存在します。分かりやすさの為、正方形内部に色を付けています。

縦横格子_combined.jpg

この時、一方のパリティに属する正方形を採用すると、他方のパリティに属する正方形は最大4つ失われてしまうということがご覧いただけるでしょうか。 上下左右の辺を、それぞれ一つずつ他方のパリティに属する正方形が使用している場合に、あわせて4つの損失ということです。

これは明らかに望ましいとは言えません。出来るだけ長方形を多く描きたいという状況において、もしそれぞれのパリティが入り乱れて使用されてしまえば、それだけ多くの候補が失われてしまいます。

結局、このパリティをどちらか一方に制限すれば、もう一方のパリティの正方形は全て使えなくなるものの、確実に最大数の正方形を使用できることが出来るので、もしかしたらこの「パリティをどちらかに決め打つ」ということは有効な手段かも知れないと考えられます。 (無論、制限があればそれだけ自由は損なわれるので、この時点ではまだ必ずしも有効な手段とは言えないです。)

斜め格子についても考えてみます。 同様に、辺の集合を被らせないということを意識すると、x座標に関するパリティによって以下の2パターンに分類することが可能です。

斜め格子_combined.jpg

これまた、一方のパリティに属する正方形を採用すると、他方のパリティに属する正方形は右上、右下、左下、左上の最大4つが失われてしまいます。

これに関してもどちらかのパリティに決め打てば、もしかしたらうまく行くかもしれません。

そして、x座標とy座標は概念として対等なので、これを90度回転し、y座標に関するパリティにのみ注目した議論も可能になってきます。

ともかく、一見するとどれも同じ長さ1の正方形ではありますが、実はこれらはいくつかのパリティによる分類を有している、という観点が重要になってきます。

前提A_3 雪崩の発生が多くの他の現象より有意に強い

ここで、とあるビジュアライザ結果を見てみましょう。 かえでさんによる解説記事にも登場していたので、ご存じの方も多いかも知れませんが、しましまさんという方が、初日の時点で極めて高いスコアのビジュアライザ結果を共有されていました。

その結果では、どうやら連鎖的に外側へ長方形が伸びていく機構が確認できます。

本来の点配置が以下のestieさんのロゴなので、元々点は全くない箇所のはずです。 このような、無から有を創り出している現象が存在するのだというヒントを、ビジュアライザから得ることが出来ます。

そして、これまた独自の用語で恐縮ですが、その連鎖的に発生していく様に注目して、この現象を雪崩と私は勝手に名前を付けて考察していました。以下でもこの用語を使用します。

その後の共有されたビジュアライザ結果を見ても、良いスコアを獲得している者はほぼ必ず雪崩が起こっているので、どうやら雪崩を発生させることが、他の方針よりも強そうだということが分かります。

(余談ですが、私は最初の3日間はビジュアライザを自分で移植してずっと手動解を作り続けていました。その過程を経ても、やはりどうにも雪崩以上に強い現象は存在しないだろうということが確認できたので、それなりに自信をもって考察することが出来ました。ビジュアライザの作成はそういった実装前の考察にも役立つので、個人的にはお勧めです)

image.png

前提A_4 雪崩の発生条件を整理する

さて、ではその強いと目される雪崩の発生を目指していきます。

結論から述べますが、雪崩は以下のような手順によって発生していきます。

gif1.gif

傾いている正方形と傾いていない正方形がおよそ交互に全ての辺(枠)を使い切るように使用していけば、雪崩は上手く発生します。

この時、先程述べた前提A_2とこの雪崩は、極めて相性が良いということがご覧いただけるかと思います。 つまり、雪崩で使用される縦横格子のパリティ、斜め格子のパリティはそれぞれ完全に固定されており、パリティを決め打つことによって雪崩も作りやすくなるという一石二鳥という構図になっています。 (背景には、辺(枠)を使い切るという共通した理由が存在している為、これは必然的です)

このことから、パリティを決め打つことで得られる利点が、自由度が失われるという欠点よりも大きく上回ることが予想され、問題を解く上での大きなヒントを得ることが出来ます。

また、特にパリティを決め打つということと雪崩の関係の観点から言えば、「他方のパリティを全く使用しない」という別の面も非常に重要になってきます。

例えば、以下のような場合を考えてみます。 左が上手く雪崩を発生させられた場合、右が他方のパリティが混入してしまった場合の概念図です。

2A0848F3-20F8-465D-9711-2124CA0A2EA6.jpg

少し試せばすぐ分かることですが、一個でも他方のパリティを有する正方形を使用してしまうと、その時点で雪崩は完全にストップしてしまい、本来得られるはずだった得点が完全に失われてしまいます。特に、外側であればあるほどスコアの重みは大きかったので、その損失は絶大です。

局所的に見ていると、なぜ他とまったく同じ形の正方形が何万点もの損失につながるのか、ということは理解しづらいですが、パリティの混入という観点から見ればこれは自明で、後々ビームサーチをする上でも重要になってきます。

なお、縦横格子の方はパリティを変えても大丈夫です。 つまり、以下の別パターンも存在します。

gif2.gif

そして同様に斜め格子についてもパリティは複数パターンあります。 しかし、実は以下の点には注意する必要があります。

前提A_5 雪崩には方向が存在する

先程の節で縦横格子と斜め格子はそれぞれ複数のパターンがあると述べましたが、実は斜め格子は縦横格子と決定的に違う点が存在しています。 実は斜め格子の扱いを決定することの方が縦横格子の扱いを決定するよりも基本的にはより重要です。

その理由は、雪崩には方向が存在するからです。

例えば、y軸に関するパリティを守ったかに思える以下のような図の場合、一見雪崩が発生してくれそうにも思えますが、よく見てもらえれば分かる通り、もう既に新たにおける点は(斜め格子では)一つもありません。

only_vertical.png

結局、雪崩が伸びる方向とは別の軸に関するパリティで斜め格子を制限しなければ、雪崩は発生しない、ということが分かります。

前提A_6 雪崩の方向を意識して、有効になる正方形を事前に列挙可能である

ここまでくれば前提Aはあと一歩です。

基本的には4方向すべてに雪崩を発生させた方が強いので、全方向に雪崩が発生できるよう、斜め格子のパリティに注意して格子を決定すると、例えば以下の通りになります。

ok_rect.png

上の図では、図の上側、右側、下側、左側のどこに属しているか、ということを大まかな目安として斜め格子のパリティを切り替えており、このようにして、雪崩の方向に注意を払いながら有効になる正方形を事前に列挙することが出来ました。

以下、またまた独自の用語ですが、このように特定のパリティの組み合わせにおける有効な正方形のことをok_rectと呼ぶことにします。

同時に、パリティの嚙み合わせという語を導入します。斜め格子に関して、パリティを変更している境界線のような意味合いです。

先程の画像をよく見て頂ければ分かる通り、実はこのように列挙したok_rect達は、最密な敷き詰めではありません。異なるパリティを組み合わせて使用している為、前提A_2で述べたようにいくつかの正方形は失われてしまいます。

この失われる正方形というのが、パリティの噛み合わせそのものと言っても良いと思われます。 つまり、パリティの噛み合わせとは、雪崩の方向を変更させるために、一体どの正方形を放棄するのか、といった意味合いを持っています。

この噛み合わせ部分は、少なければ少ないほど多くの正方形を描けるので、あえて雪崩の向きを無視して噛み合わせ部分を減らすということも考えなくてはなりません。

(このように、パリティの嚙み合わせに関する議論は極めて重要なのですが、詳細は一旦省略します)

なお、これらok_rectを「事前に列挙可能である」ということは、それなりの意味を有します。 多くの場合、適当にやってしまうと、ある時点での合法手から一個取り出して採用すると、他の合法手も消えてしまうということが発生します。 これは同じ辺を共有している候補が複数含まれている場合、その内のどれか一つだけしか採用することが出来ないためです。

しかし、このok_rectに関しては最初から辺を共有せず、過去にどのような正方形を採用したかに依存しない列挙の仕方であるため、(上手く実装すれば)一回一回候補を更新する必要がなくなり、大幅な高速化が望めることになります。

実際、純粋なビームサーチを実装した場合、この合法手の更新が実行時間の9割を占めていたため、この高速化は割と本質的でした。


以上で前提Aは終了です。

この前提Aは、他にも多くの方に言及されており、本問の考察において重要なものとなっています。以下にその一部を紹介します。

makibishiさん

yUNIXさん

eijirouさん

fuppy_kyoproさん