aacord’s memo

abcを中心にpythonで解いた問題のメモ、整理をしています。緑になった。

abc 060 D-Simple Knapsack (python)

タイトルからして dp だと思ったら解説は全探索だった。本質的には dp もメモ化全探索みたいなところあるけど。
w_i の制約が特徴的で max(w_i) - w_1 <= 3 とある。なので
dp[ i ][ j ][ k ] で i 番目の荷物まで見たときに、j 個の荷物を入れて、
重さが j * w_1 + k となっているときの価値の最大値
として dp を作ってみた。
(k は選んだ荷物の重さの合計 - 選んだ数 j * 荷物の最小値 w_1で与えられる)
そうすることで重さが w を超えるような選び方を排除しつつ、
最大 100*100*(3*99) で計算できるようになった。
(荷物の数は100以下で、k が最大となるのは w_2,...,w_n が全て w_1+3 となる時)
最大値は dp[ n ] のどこかにあるので、思考停止して dp[ n ] を全て展開して最大値を求めた。
初期値を -float('inf') にしているが、dp[ i ][ 0 ][ 0 ] だけは全て 0 にできるように実装するのに注意。

import sys
input = sys.stdin.readline
import itertools
n,w = map(int,input().split())
c = []
s = 0
for i in range(n):
  a,v = map(int,input().split())
  s += a
  c.append((a,v))
  
r = c[0][0]
L = min(100,w//r)
m = s-r*n+1
dp = [[[-float('inf')]*m for i in range(L+1)] for i in range(n+1)]
for i in range(n+1):
  dp[i][0][0] = 0

for i in range(n):
  for j in range(L):
    for k in range(m):
      W,V = c[i]
      R = W-r
      if k-R >= 0 and r*(j+1)+k <= w:
        dp[i+1][j+1][k] = max(dp[i][j+1][k],dp[i][j][k-R]+V)
      else:
        dp[i+1][j+1][k] = dp[i][j+1][k]
        
print(max(list(itertools.chain.from_iterable(dp[n]))))