AcWing-3. 完全なナップザック問題 (Java 実装)

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_kf ( 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 jv i )+_f (1 j2v i )+2_f (1 jkv 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 jv i )+_f (1 j2v i )+2_f (1 jkv 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 ,jv i )=              Max ( f ( i _ _1 jv i )          f (1 j2v i )+_f (1 jkv i )+( k1 )ウィ_

これを組み合わせると、最終的な状態遷移方程式が得られます。
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 ,jv 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 ,jv 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 ( jv 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]);
	}
}

おすすめ

転載: blog.csdn.net/gudada010/article/details/117490503