2019年6月16日日曜日

diverta 2019 Programming Contest 2 - D Squirrel Merchant

diverta 2019 Programming Contest 2 - D Squirrel Merchant

やることは次の2つのステップにわかれている:

  1. 取引所Aでどんぐりを金属に交換し、取引所Bで金属をどんぐりに交換する
  2. 取引所Bでどんぐりを金属に交換し、取引所Aで金属をどんぐりに交換する

それぞれのステップでどんぐりを最大化すればよい。

また、gAgBg_A \le g_Bなら金を交換するのはステップ1のみでよいし、gAgBg_A \ge g_Bならステップ2のみでよい。銀、銅についても同様である。つまり、交換レートの大小関係に応じて、次の3つの場合を考えればよい。

gAgBg_A \le g_B, sAsBs_A \le s_B, bAbBb_A \le b_Bの場合

ステップ1で金銀銅を交換し、ステップ2では何もしないのが最適である。

Aで金をxxグラム、銀をyyグラム買うとすると、銅は最大で(NxgAysA)/bA\lfloor (N - xg_A - ys_A)/b_A \rfloor買えるので、最終的などんぐりの数が決まる。xgA+ysANxg_A + ys_A \le Nという条件下で最大値を全探索すればよい。O(N2){\mathcal O}(N^2)

gAgBg_A \le g_B, sAsBs_A \le s_B, bAbBb_A \ge b_Bの場合

ステップ1で金銀を交換し、ステップ2で銅を交換するのが最適である、

ステップ1で金をxxグラム買うとすると、銀は最大で(NxgA)/sA\lfloor (N - xg_A )/s_A \rfloorグラム買えるので、ステップ1終了後のどんぐりの数が決まる。xgANxg_A \le Nという条件下で最大値を全探索すればよい。

ステップ1終了後のどんぐりの数の最大値をMMとする。ステップ2ではできるだけ多くの銅を交換するのがよいため、最終的な最大値はM/bBbA+M%bB\lfloor M/b_B \rfloor \cdot b_A + M\%b_Bである。(ただし、%\%はあまりを求める演算子とする。)計算量はO(N){\mathcal O}(N)

gAgBg_A \le g_B, sAsBs_A \ge s_B, bAbBb_A \ge b_Bの場合

ステップ1で金を交換し、ステップ2で銀銅を交換するのが最適である、

ステップ1ではできるだけ多くの金を交換するのがよく、どんぐりの数の最大値はM:=N/gAgB+N%gAM := \lfloor N/g_A \rfloor \cdot g_B + N\%g_Aである。

ステップ2で銀をxxグラム買うとすると、銅は最大で(MxsB)/bB\lfloor (M - xs_B )/b_B \rfloorグラム買えるので、最終的などんぐりの数が決まる。xsBMxs_B \le Mという条件下で最大値を全探索すればよい。計算量はO(M)=O(NgB){\mathcal O}(M) = {\mathcal O}(Ng_B)。□

ほかのパターンは適当に定数を入れ替えれば上のどれかに帰着する。c:=max(gA,gB,sA,sB,bA,bB)c := \max(g_A, g_B, s_A, s_B, b_A, b_B)とすると、全体の計算量はO(Nmax(N,c)){\mathcal O}(N\max(N, c))である。


この方針は金属が3種類しかないことに依存していて、4種類だと早くも破綻してしまう。1

3種類なら場合分けが通用するだろうというあたりから考察を始めるので、想定解のDPは一瞬も考えなかった。それはそれでいいとしても、たとえばkk種類だったらDPを書けただろうか? さすがに書けると思いたいけど、あまり自信はない。


  1. 深く考えてないけど、2-2, 3-1の買い方は上と同じ方針で良さそう。0-4, 4-0の買い方は半分全列挙で何とかなる? でも、1-3が間に合わない気がする。 ↩︎