Educational DP Contest
問題
方針
\(N\)が小さく、\(W\)が大きい。また\(v_i\)も大きいので、計算量が\(O(NW)\)となるようにする。蟻本にあるように、\((N+1)(W+1)\)の配列を作る。二次元配列以上だとnumpyが便利なので、PyPy3は使えない。
解答
#another
#atcoder template
def main():
import sys
input = sys.stdin.readline
#文字列入力の時は上記はerrorとなる。
#ここにコード
n, W = map(int, input().split())
import numpy as np
dp = np.zeros((n+1, W+1), np.int64)
for i in range(n):
w, v = map(int, input().split())
np.maximum(dp[i, w:], dp[i, :-w]+v, out = dp[i+1, w:])
dp[i+1, :w] = dp[i, :w]
print(dp[-1, -1])
#N = 1のときなどcorner caseを確認!
if __name__ == "__main__":
main()
提出結果
補足
DP tableが更新される様子を入力例3で確認してみる。最初に作られるのは以下のようなテーブル。
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
これは行が\(6+1\)、列が\(15+1\)の行列と考えると簡便である。for loopを回すと
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
ここでは\(1\)行目(Pythonのindexは\(0\)から始まることに注意)
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 12, 12, 12, 12],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 12, 12, 12, 12],
[ 0, 0, 0, 5, 5, 5, 6, 6, 6, 11, 11, 11, 12, 12, 16, 17],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11],
[ 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 11, 12, 12, 12, 12],
[ 0, 0, 0, 5, 5, 5, 6, 6, 6, 11, 11, 11, 12, 12, 16, 17],
[ 0, 0, 0, 0, 0, 0, 0, 6, 6, 11, 11, 11, 12, 12, 16, 17]])
のように更新されていく。よりわかりやすく、枠で囲むと以下のようになる。
このときは、\(w_0= 6\)なので、黄色の枠がindex\(0\)からindex\(-7(-6-1)\)まであって、緑の枠がindex\(6\)から最後まである。黄色の配列に\(v_0 = 5\)を足した\([5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\)と緑の配列\([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\)の大きい方を取った配列は\([5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\)になるので、赤い部分は\([5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\)になる。
このときは、\(w_1= 5\)なので、黄色の枠がindex\(0\)からindex\(-6(-5-1)\)まであって、緑の枠がindex\(5\)から最後まである。黄色の配列に\(v_1 = 6\)を足した\([6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11]\)と緑の配列\([0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\)の大きい方を取った配列は\([6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11]\)になるので、赤い部分は\([6, 6, 6, 6, 6, 6, 11, 11, 11, 11, 11]\)になる。後は以下同様であるので、画像だけ載せる。
配列の一番最後が、求める値\(17\)になっている。
コメント