HTTF2019本選:モンスターテイマー

オープン4位だったので記念に投稿。本選の人と合わせると14位かな?
(12/03:4億点が出たので追記しました)

問題

A - モンスターテイマー

 モンスターの高橋君(?)を育てながら,野良モンスターを討伐し,所持金を増やすという問題。

f:id:sigma1113:20181201233818p:plain:w300
初期状態の高橋君

正の得点を得る

 この問題では,正の得点を得るのは容易だ。高橋君も討伐依頼も無視し,ひたすらアルバイトする。
Submission #3692024 - HACK TO THE FUTURE 2019 本選オープン

レベル上げは重い...?

 問題文をぱっと見ると,レベル上げのコストがかなり重そうに見えた。そこでとりあえず,討伐依頼のうち,必要なスキルレベルの最大値が1であるもののみを相手にすることにしてみる。最初はすべてのスキルのレベルを1にすることを目指し,その後はひたすら討伐する。テストケースの作り方より,相手にする討伐依頼は30000\times 0.1=3000個くらいだろう。
 提出してみると14509点で思ったより良かった。
Submission #3692352 - HACK TO THE FUTURE 2019 本選オープン

それでもレベルは上げたほうがいい

 この時点ですでに2億点に乗せている人がいたため,何かを勘違いしているのだろうと察する。どうやら,レベルは上げてしまった方がいいらしい。簡単に見積もってみる。すべてのスキルのレベルを10にするのに必要な金額は
10\sum_{k=1}^{10}k\cdot 2^k = 1.8434\times 10^9
やはり重いように見えるが,ここですべてのスキルを上げたときに,討伐によってどれくらいの報酬が見込めるかを考えてみる。それは,テストケースの作り方から見積もれる。まず,1000ターンにたいして依頼は30000個あるから,各ターンに30個はそのターンが締め切り日であるような依頼があると考えてよい。また,30個もあれば,1つくらいは必要スキルの総和が50くらいのものがあることを期待することにする(そんなに変な期待ではないはず...)(レベルmaxなので,各ターンで報酬が最大のものを取るのが基本になることに注意,もちろん実際に見るのはC_iの値だが)。さらに,スキルボーナスも必ず獲得できる。そうすると,討伐によって見込める利益は
(1.3^{50}\times 1000) \times 10 \times 10 \approx 4.9\times 10^{10}
であり,たった1回の討伐で元が取れそうとわかる。(ちなみに実際に平均を取ってみると3.0\times 10^9くらいでした,すいません。一応元は取れていますが。)
要は,

  • 前半はできるだけ早くすべてのスキルをmaxにする
  • 後半は討伐内容をうまく選んで,報酬を最大化する

という問題にしてしまった方が,後半でがっぽり稼げるので最終的にもスコアが高くなるのである(基本はそうというだけで、実際は後述するようにレベルを上げながら何をするかということも重要になってくる)。すぐには確信できない部分はあったが,時間はあるので物は試しという気持ちでとりあえず実装する。もう少し詳しく書くと

  • 討伐依頼のリストは締め切り時刻ごとに管理する。
  • レーニングするお金がないとき:その時点で報酬が最も高い討伐をするか,アルバイト
  • レーニングするお金があるとき:トレーニングする。すべてのスキルをレベル1に→すべてのスキルをレベル2に→...→すべてのスキルをレベル10に となるようにトレーニングする。スキル1から順番にレベルを上げる。
  • すべてのスキルレベルが10になったら,そこからは各ターンで最も高い報酬の得られる討伐を行う。

これを実装して提出するとなんと2.9億点!ナイーブな発想だが,これがちゃんとできるかが一つの山だったようである。
Submission #3693049 - HACK TO THE FUTURE 2019 本選オープン

f:id:sigma1113:20181202160326p:plain:w300
レベル10の高橋君

地道な工夫

 ここからはろくなことができなかったので,実際にやった地道な工夫を紹介。

  • レベル上げの途中にも,おいしい討伐依頼が転がっている可能性がある。そのため,報酬がある閾値sを超えるような討伐依頼は受けるということをする。計算時間が余っていたので,このsは適当そうな範囲を探索した。
  • レベル上げが終わってからは,受ける討伐依頼を前から決めるより,後ろから決めた方がスコアがよかったのでそうした。この貪欲も厳密ではない(短期間の依頼は報酬の落ち方が激しいため,その分損をする可能性がある)。
  • 全く本質的ではないが,スキル上げの順番を2通り(だけ)試していい方を選んだら少し上がった。

というわけで,最終結果となった提出がこれだった。
Submission #3694390 - HACK TO THE FUTURE 2019 本選オープン

反省と感想

  • レベル上げの順番を焼きなましやら山登りやらするといいらしい。試してみていい結果がでたら加筆します。
  • レベル上げの途中にどの討伐依頼を受けるかというところでも工夫の余地がある。
  • 後ろから貪欲に加えて1つ前のターンとの差分を考えるとうまくいくらしい。
  • 楽しかった。来年は新卒枠なので賞金を狙えるくらいになりたい...。

追記(12/03):山を登る

 山登り法を試したところ効果があったので,その詳細を述べる。
レベル上げの順番を最適化することを考える。レベル上げの順番は,1から10のスキルの番号を10個ずつ持つ大きさ100の配列pで管理する。例えば,配列の\{\dots,1,3,10,1,\dots\}という部分は,「スキル1のレベルを1上げる→スキル3のレベルを1上げる→スキル10のレベルを1上げる→スキル1のレベルを1上げる」ということを意味する。pの初期値をてきとーに設定し,そこから2つの要素をswapした配列に対し1000ターン分のスコアを計算し,良くなれば採用,ということを制限時間いっぱいまで繰り返す。
 山登り法では,できるだけシミュレーション回数を増やしたいため,サボれるところはサボることを考える。レベル上げ終了後は,レベルをどういう順番で上げたかということに依存せずに行動を決定できる。今回は,レベル上げ終了後は「ターンの遅い順に討伐する依頼を決定する」という方針を引継ぐことにする。山登りを始める前に,tターンからレベル上げ後の行動に移るときの報酬の総和と行動を事前に求めておけば,だいたい350\sim 400ターン分くらいはサボれることになる。
 ここまでやったのが以下。3.3億点で,やはり「レベル上げの間でも取るべき報酬は取る」ということが必要そうだ(とはいっても,レベル上げの順番を考えないよりははるかに高いスコアだ)。
Submission #3707961 - HACK TO THE FUTURE 2019 本選オープン

 では,レベル上げの間に取るべき報酬をどう決めるかということを考える。なぜわざわざレベル上げの途中に報酬を貰いに行くかというと,その報酬がレベル上げ後に取る報酬といい勝負だからであろう。よって,レベル上げ後に取る報酬を用いてこの基準を定める。具体的にどう決めるかはいろいろあるが,
0.55\times「(今からストレートでレベル上げが終わるターン+10ターン)後に得る報酬」
以上の報酬であれば,取りに行くことにした。0.55や「10ターン」という謎の数字はエスパーで決めた。
 これを提出すると,運が少し良いと4億に乗る(だめでも3.8億はある)。安定してより高いスコアを出すにはさらなる工夫が必要そう...。
Submission #3708880 - HACK TO THE FUTURE 2019 本選オープン

追記(12/07):焼きなまし法を試したら4.13億に到達。パラメータ調整が難しすぎる。今は温度を減らしていない(は?)のでまだいけるかも?
Submission #3730828 - HACK TO THE FUTURE 2019 本選オープン

おまけ

ビジュアライザでは4種類の高橋君が登場するらしいのでコンプした。うち2種類は上に挙げたので省略。

分岐1

f:id:sigma1113:20181202200935p:plain:w300
かわいい
f:id:sigma1113:20181202201203p:plain:w300
かわいい
f:id:sigma1113:20181202201300p:plain:w300
空気変わったよ...
f:id:sigma1113:20181202201334p:plain:w300
子供の恐竜感
f:id:sigma1113:20181202201410p:plain:w300
BLUE DRAGONと聞いて何を連想する...?
f:id:sigma1113:20181202201607p:plain:w300
ドラゴンボールのあれみたい

分岐2

f:id:sigma1113:20181202201511p:plain:w300
森にいそう
f:id:sigma1113:20181202201626p:plain:w300
飼いたい
f:id:sigma1113:20181202201650p:plain:w300
倒すと宝石が貰えそう
f:id:sigma1113:20181202201720p:plain:w300
ゲームの中盤くらいからモブで出てきそう
f:id:sigma1113:20181202201837p:plain:w300
こっちは緑系ですね
f:id:sigma1113:20181202202022p:plain:w300
GREEN DRAGONでした