[学習記録]AtCoder EDP E-Knapsack 2[Go]
問題
問題文
N 個の品物があります。 品物には 1,2,…,N と番号が振られています。 各 i (1≤i≤N) について、品物 i の重さは wi で、価値は vi です。
太郎君は、N 個の品物のうちいくつかを選び、ナップサックに入れて持ち帰ることにしました。 ナップサックの容量はW であり、持ち帰る品物の重さの総和は W 以下でなければなりません。
太郎君が持ち帰る品物の価値の総和の最大値を求めてください。
制約
入力はすべて整数である。
1≤N≤100
1≤W≤10^9
1≤wi≤W
1≤vi≤10^3
解法
問題文はD - Knapsack1と同じだが、制約が異なる。そのため、Dと同じ解法で行うとTLEとなる。
価値の総和の最大は、10^ 3 × 100 = 10 ^5と、使えそうなので、こちらを用いて問題を解く。
i 番目までの品物を使って、ちょうど価値 j にしたときの最小重さを記録しておくと、価値が高いほうから最小重さを見ていき、初めてW以下となったときの価値が回答になる。
dp := make([][]int, n+1)
max_value := 1_000 * 100
ans := 0
for i := max_value; i >= 0; i-- {
if dp[n][i] <= w {
ans = i
break
}
}
dpの計算はDと同じように、i -1番目を入れた場合、入れない場合での最小重さを計算する。ただし、dpの初期化は重さの最大を超える値でしておく。
そもそも選択できないときのガードも同様に追加する。
dp := make([][]int, n+1
over_weight := 1_000_000_000*100 + 1
dp[0] = initSliceAs(max_value+1, over_weight)
for i := 1; i <= n; i++ {
dp[i] = initSliceAs(max_value+1, over_weight)
weight := sli[i-1][0]
value := sli[i-1][1]
for j := 1; j <= max_value; j++ {
if j-value < 0 {
dp[i][j] = dp[i-1][j]
continue
}
// 入れる
in := dp[i-1][j-value] + weight
// 入れない
not_in := dp[i-1][j]
dp[i][j] = Min(in, not_in)
}
}
品物を入れないときは 価値 = 0とするため、dpの初期値を 0 で更新する。
dp[0] = initSliceAs(max_value+1, over_weight)
dp[0][0] = 0
// i loop
for {}
// 入力例3の時のdp(抜粋)
[0 100000000001 100000000001 100000000001 100000000001 100000000001 100000000001 100000000001 100000000001 100000000001]
[0 100000000001 100000000001 100000000001 100000000001 6 100000000001 100000000001 100000000001 100000000001]
[0 100000000001 100000000001 100000000001 100000000001 6 5 100000000001 100000000001 100000000001]
[0 100000000001 100000000001 100000000001 6 6 5 100000000001 100000000001 12]
[0 100000000001 100000000001 100000000001 6 6 5 100000000001 100000000001 12]
[0 100000000001 100000000001 100000000001 6 3 5 100000000001 100000000001 9]
[0 100000000001 7 100000000001 6 3 5 10 12 9]
解法のポイント
制約から vを添え字として、そして入れ替えできる可能性から最小値をdpとして持つことから、解法を考える。
コード(AC)
func run() interface{} {
n, w := readInt(), readInt()
sli := make([][]int, n)
for i := 0; i < n; i++ {
sli[i] = readSli(2)
}
dp := make([][]int, n+1)
max_value := 1_000 * 100
over_weight := 1_000_000_000*100 + 1
dp[0] = initSliceAs(max_value+1, over_weight)
dp[0][0] = 0
for i := 1; i <= n; i++ {
dp[i] = initSliceAs(max_value+1, over_weight)
weight := sli[i-1][0]
value := sli[i-1][1]
for j := 0; j <= max_value; j++ {
if j-value < 0 {
dp[i][j] = dp[i-1][j]
continue
}
in := dp[i-1][j-value] + weight
not_in := dp[i-1][j]
dp[i][j] = Min(in, not_in)
}
}
ans := 0
for i := max_value; i >= 0; i-- {
if dp[n][i] <= w {
ans = i
break
}
}
return ans
}
反省点等
dpの初期化を 0 で行ったり、dp[ i ][ j ] = (価値がjを超えるように選んだ時の最小重さ)としたときでも解くことはできたが、余計な処理を多分に入れる必要があった。
この記事が気に入ったらサポートをしてみませんか?