Processing math: 100%

AtCoder Beginner Contest 373 F問題メモ

F - Knapsack with Diminishing Values

問題文

  • N 種類の品物があり、 i 種類目の品物の重みは wi、価値は vi です。どの種類の品物も 1010 個ずつあります。
  • 品物をいくつか選んで、容量 W のバッグに入れます。
  • ただし、各品物につき、k 個選ぶと k2 のペナルティが科されます。
    • つまり、i 種類目の品物を ki 個選んだとき、スコアは kivik2i
  • 選んだ品物の重さの総和を W 以下にしつつ、達成できるスコアの総和の最大値 Ni=1kivik2i を求めてください。

制約

  • 1N3000
  • 1W3000
  • 1wiW
  • 1vi109
  • 入力はすべて整数

解法

以下の素直なナップサックDPを考える。

  • DP[i,j]:=i 番目の品物まで考慮し、重さ合計が j の場合の最大スコア

通常のナップサックDP(のうち、同じ品物を何個でも選んで良い版)では、 各 i につき、j が小さい方から DP[i,j+wi]maxDP[i1,j]+vi と 更新していくことで O(W)、全体 O(NW) で更新できる。

しかし、今回は加算されるスコア vi が、それまで何個の同じ品物を選んだかによって変化するので上手くいかない。
(そして、その個数別にDPを管理してたら、O(NW2) となり間に合わない)

以下の性質を利用する。

DP[i,j] は、もらうDPで考えて、以下の中の最大値である。

  • DP[i1,j0wi]+0vi02
  • DP[i1,j1wi]+1vi12
  • DP[i1,j2wi]+2vi22
  • DP[i1,j3wi]+3vi32
            j-4w j-3w j-2w  j-w   j
DP[i-1]   ....x....x....x....x....x
               ↘   ↘   ↘   ↘  ↓
DP[i]                             *

DP[i,j] の更新にあたり最大値を取る更新元(jkiwi)を argmax(j) とする。

ここで、以下の事実が成り立つ。

  • ある (j1,j2) の組について、modwi が等しく、j1<j2 ならば、argmax(j1)argmax(j2) である。

略証

modwi が等しい同士にグループ分けすると、j を増やすに従って argmax(j) は必ず広義単調増加する。

このような性質を Monotone といい、各 j に対する最大値は Monotone Minima を使うことで O(WlogW) で求められる。

よって、全体で O(NWlogW) に高速化される。

Python3

programming_algorithm/contest_history/atcoder/2024/0928_abc373.txt · 最終更新: 2024/10/07 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0