ICFPC2010参戦記

問題設定とかは http://d.hatena.ne.jp/ku-ma-me/ を見ればいいとして、

金曜日21:00〜23:00

問題読みタイム。さっぱりわからないが、一番下に最初にやるべきことが書いてあったので、0を送ればいいっぽい、サーバが死に掛けてて全く送れない。やっと反応があったときにはすでに登録されてるよって出る。

それはいいとして、次は回路を理解せよということだったので、まず回路のフォーマットを推測して、適当に作った回路を送り始める。「22022022022022022」を返してくる回路を見つけてこれがヒントになるかと思ったが、時間切れで電車に乗る

土曜日

帰宅して、適当に回路を作って送りつける作業を続ける。結論として、次のような回路を考えればゲートの動作を推測できることが分かった。

  X +---+
  | |   |
+-*-*-+ |
|     | |
+-*-*-+ |
  | |   |
  | +---+
  A
  | +---+
  | |   |
+-*-*-+ |
|     | |
+-*-*-+ |
  | |   |
  | +---+
  B
  | +---+
  | |   |
+-*-*-+ |
|     | |
+-*-*-+ |
  | |   |
  | +---+
  C

最初に1ゲートのを送ってAの出力を調べて、次に2ゲートのを送ってBの出力を調べて、を繰り返せば入力と出力のペアはいくらでも得られる。これを左右反転した回路に対しても行って、

左入出力
02120112100002120
00100202221110100
01221000201200221
02110011002121022
00122120010101102
01211021122222000
02101101211111211
00112002101201012
右入出力
22022022022022022
21202210221202210
20221022020221022
22102202212020221
21121022111202211
20202202021120202
22120221202112022
21112020221102210

これをじっと眺めてれば、左に2を入力したときに左からは0,1,2の全通りが出てるので、

in  | out
2 ? | 0 X
2 ? | 1 ?
2 X | 2 ?

よく見ると、「2->0」の直後に「2->2」があるので、Xが等しいことが分かる。途中で計算間違いを莫大にしたので時間がかかったけど、記録によると1:30ごろにゲートは確定したようだ。

回路作成

というわけで、今度は任意の出力を出せる回路を作る方法を考えないといけないわけだが、最初に考えたのは常に0を出す基本回路を作るんじゃねとか思ってみたりしたんだけど、どう頭をひねっても想像もできないので、やめてもっと直接にやる方法を思いついた。

とりあえず、回路全体をこの形に固定することにする

 inp
  | +---+
  | |Y  |
+-*-*-+ |
|     | |
+-*-*-+ |
  | |X  |
  | +---+------X
  |     |         残りの回路
 out    +------Y'

この回路はinpとoutのtrit列が与えられたとき、 inp[0] = out[0] ならば解があって、

Y = 0::Y'
inp[0] = out[0]
X[0] = inp[0]×0-1
Y[k] = inp[k]-out[k]
X[k] = inp[k]×Y[k]-1

のように、XとY'のtrit列を完全に決定できる。しかも、Y'の列の長さはoutの長さより1短い。

よって、inpからoutを出す回路を作る問題はinp[0]=out[0]のときは、1短い入出力の問題に単純化できた。

同様に inp[0]-2=out[0]のときは、ちょっと改造して Y = 2::Y' にすればいいので、

    +---+ +-------+
    |   | |       |
    | +-*-*-+     |
    | |     |     |
    | +-*-*-+     |
    |   | |       |
    +---+ |       |
          |       |
        +-+       |
        |         |
        | +---+   |
        | |   |   |
      +-*-*-+ |   |
      |     | |   |
      +-*-*-+ |   |
        | |   |   |
        | +---+---+
 inp    |     |
  | +---+     |
  | |Y        |
+-*-*-+       Y'
|     |
+-*-*-+
  | |X
  | +-----X
  |
 out

とつなぐと、Y = 2::f(Y') となる。このfが全単射になるように繋がないといけないので右出力を使えないのです。

これでkey prefixは通った。朝の7時半ごろ。そのまま寝て起きて、夜になったら車フォーマットを研究することにする。

日曜日

午前2時ぐらいにやっと車のフォーマットがほぼわかった。引き続き燃料の解析。午前3時半に最初の燃料が通過する。この時点で簡単な車にがんがん給油してればもっと点数も高かったんだけど、メインの問題を考え始めてしまった。一人でやってるとしかたがないよね。

このへんで、サンプルの車を解こうとするが、さっぱり理解できない。こんな車解けるのか?ランダムに行列を生成したり、ちょっとした変異を加えても全く答えに近づいているような気配がない。9時半まで悩んで全く解決しなかったので寝る。

21時ごろに起きて、大衆車に給油する仕事をすませる。そのあとサンプル車の解き方を考えるのだが、こういう問題の基礎知識がないのでさっぱりわからない。非線形最適化問題とかやったことないよ。

23時にあきらめてWORKINGを見たりする。さすがに時間切れ。