AcWing-3. 完全なナップサック問題
トピックの説明
N個のアイテムと容量Vのナップザックがあり、各アイテムは無限に入手可能です。
i 番目の項目の体積は vi、値は wi です。
リュックサックの容量を超えず、合計値が最大になるように、リュックサックにどのアイテムを入れるかを考えます。
最大値を出力します。
入力フォーマット
最初の行では、2 つの整数 N と V がスペースで区切られており、それぞれアイテムの数とバックパックの体積を表しています。
次に N 行があり、各行にはスペースで区切られた 2 つの整数 vi、wi があり、それぞれ i 番目の項目の量と値を表します。
出力フォーマット
最大値を表す整数を出力します。
データ範囲
0<N,V≤1000
0<vi,wi≤1000
入力サンプル:
4 5
1 2
2 4
3 4
4 5
出力例:
10
アイデア(プレーンバージョン)
状態表現: f ( i , j )、容量が j を超えない、1 ~ i から選択されたすべての値セットを表します。 状態表現: f(i,j)、容量が j を超えない、1 ~ i から選択されたすべての値セットを表します。状態表現: f ( i ,j ) 、1からiまで選択された、容量がjを超えないすべての値セットを表します。
属性: 最大 属性: 最大属性:最大_ _
コア: 最後の番号 i を取得しない、または k 個を取得します (k の最大値は、i の最大可能数です、つまり、i を k 個取得した後の総容量が j を超えません) コア: 最後の番号 i を取得しません。最後の番号 i または k 個を取る (k の最大値は i の最大可能数です。つまり、k i を取った後の総容量は j を超えません)コア:最後の番号iが取得されないか、 k が取得されます( kの最大値はiの最大可能数です、つまり、k i を取得した後の総容量はjを超えません)
状態分割: f ( i − 1 , j ) は 0 i, 1 i ... ... 、 k i は f ( i , j ) になります。 状態分割: f(i-1,j) は 0 i, 1 i を取ります。 ..,k i は f(i,j) になります州の分割:f (私−1 、j ) 0 i , _を取る1私_……、k私はf ( i , _j )
状態遷移方程式は状態分割によって取得できます。
f ( i , j ) = Max ( f ( i − 1 , j ) , f ( i − 1 , j − vi ) + wi , f ( i − 1 , j − 2 ∗ vi ) + 2 ∗ wi , … … , f ( i − 1 , j − k ∗ vi ) + k ∗ wi ) f(i,j) = Max(f(i-1,j),f(i - 1,j-vi)+wi,f(i-1,j-2*vi)+2*wi,...,f(i-1,jk*vi)+k*wi)f ( i ,j )=Max ( f ( i _ _−1 、j ) 、f (私−1 、j−v i )+私、_f (私−1 、j−2∗v i )+2∗私、_……、f (私−1 、j−k∗v i )+k∗ウィ)_
コアコード
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
for(int k=1;k<=j/a[i].v;k++) {
dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*a[i].v]+k*a[i].w);
}
}
}
}
完全なコード
package acWing003;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[][] = new int[N][N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
for(int k=1;k<=j/a[i].v;k++) {
dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*a[i].v]+k*a[i].w);
}
}
}
}
System.out.println(dp[n][v]);
}
}
class Pair{
int v,w;
public Pair(int a,int b){
v = a;
w = b;
}
}
class Read{
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public int nextInt() throws Exception{
st.nextToken();
return (int)st.nval;
}
}
最適化(状態の最適化)
状態遷移方程式は状態分割によって取得できます。
f ( i , j ) = Max ( f ( i − 1 , j ) , f ( i − 1 , j − vi ) + wi , f ( i − 1 , j − 2 ∗ vi ) + 2 ∗ wi , … … , f ( i − 1 , j − k ∗ vi ) + k ∗ wi ) f(i,j) = Max(f(i-1,j),f(i - 1,j-vi)+wi,f(i-1,j-2*vi)+2*wi,...,f(i-1,jk*vi)+k*wi)f ( i ,j )=Max ( f ( i _ _−1 、j ) 、f (私−1 、j−v i )+私、_f (私−1 、j−2∗v i )+2∗私、_……、f (私−1 、j−k∗v i )+k∗ウィ)_
f ( i , j − vi ) = Max ( f ( i − 1 , j − vi ) , f ( i − 1 , j − 2 ∗ vi ) + wi , … … , f ( i − 1 , j − k ∗ vi ) + ( k − 1 ) ∗ wi ) f(i,j-vi) = ~~~~~~~~~~~~~Max(f(i-1,j-vi),~~ ~~~~~~~f(i-1,j-2*vi)+wi,……,f(i-1,jk*vi)+(k-1)*wi)f ( i ,j−v i )= Max ( f ( i _ _−1 、j−v i ) 、 f (私−1 、j−2∗v i )+私、_……、f (私−1 、j−k∗v i )+( k−1 )∗ウィ)_
これを組み合わせると、最終的な状態遷移方程式が得られます。
f ( i , j ) = Max ( f ( i − 1 , j ) , f ( i , j − vi ) + wi ) f(i,j) = Max(f(i -1,j),f(i,j-vi)+wi)f ( i ,j )=Max ( f ( i _ _−1 、j ) 、f ( i ,j−v i )+ウィ)_
コアコード
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
dp[i][j] = Math.max(dp[i][j],dp[i][j-a[i].v]+a[i].w);
}
}
}
完全なコード
package acWing003;
// 读入和Pair类都在朴素版
public class Main2 {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[][] = new int[N][N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
dp[i][j] = Math.max(dp[i][j],dp[i][j-a[i].v]+a[i].w);
}
}
}
System.out.println(dp[n][v]);
}
}
最適化 (ローリング配列の次元削減を使用)
結合された最終状態遷移方程式は次のとおりです。
f ( i , j ) = Max ( f ( i − 1 , j ) , f ( i , j − vi ) + wi ) f(i,j) = Max(f( i- 1,j) 、f(i,j-vi)+wi)f ( i ,j )=Max ( f ( i _ _−1 、j ) 、f ( i ,j−v i )+w i )
状態方程式から、現在の行と前の行のみを使用する必要があることがわかります。そのため、ローリング配列の概念を使用して、2 次元を 1 次元に減らすことができます。つまり、 f (
j ) = Max ( f ( j ) , f ( j − vi ) + wi ) f(j) = Max(f(j),f(j-vi)+wi)f ( j )=Max a x ( f ( j ) ,f ( j−v i )+ウィ)_
コアコード
for(int i=1;i<=n;i++) {
for(int j=a[i].v;j<=v;j++) {
dp[j] = Math.max(dp[j],dp[j-a[i].v]+a[i].w);
}
}
完全なコード
package acWing003;
// 读入和Pair类都在朴素版
public class Main3 {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[] = new int[N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=a[i].v;j<=v;j++) {
dp[j] = Math.max(dp[j],dp[j-a[i].v]+a[i].w);
}
}
System.out.println(dp[v]);
}
}