揹包九講問題
一.01揹包
題鏈接
問題分析:
dp問題我們一般都是可以採用遞推的方式
對於二維揹包問題,
f[i][j]:表示在看前i個物品的前提下,總體積爲j是最大價值
那麼f[i][j]的值爲:
在考慮前i個物品時並不代表一定要把所有的都算進去
①當第i個物品不算進去時,f[i][j]=f[i-1][j]
②當把第i個物品算進去時,f[i][j]=f[i-1][j-v[i]]+w[i]
result = max(f[N][0-v])
初始化:f[0][0] = 0
import java.util.Scanner;
public class Math {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int N,V;
N = scan.nextInt();
V = scan.nextInt();
int []w = new int[N+1];
int []v = new int[N+1];
int f[][] = new int[N+1][V+1];
for(int i=1;i<=N;i++)
{
v[i] = scan.nextInt();
w[i] = scan.nextInt();
}
//初始化
f[0][0] = 0;
for(int i = 1;i<=N;i++)//i從1開始的原因是防止f[i-1][j]越界
for(int j=0;j<=V;j++)
{
f[i][j] = f[i-1][j];
if(j>= v[i])
{
f[i][j] = java.lang.Math.max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
int res = 0;
for(int i = 1;i<=N;i++)
{
res = java.lang.Math.max(res,f[i][V]);
}
System.out.println(res);
}
}
代碼優化:
優化:不用二維,而用一維
f[j]:表示在總體積爲j的情況下的最大價值
import java.util.Scanner;
public class Math {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int N,V;
N = scan.nextInt();
V = scan.nextInt();
int []w = new int[N+1];
int []v = new int[N+1];
int f[] = new int[V+1];
for(int i=1;i<=N;i++)
{
v[i] = scan.nextInt();
w[i] = scan.nextInt();
}
//初始化
f[0] = 0;
for(int i=1;i<=N;i++)
for(int j=V;j>=v[i];j--)//保證f[j-v[i]等於原來的那個f[i-1][j-v[i]],如果j<v[i]那麼直
//接進入下一個i;而自然的在這個j前面的部分的f[0,1,2....,j-1]還是等於原來的f[j],保持不變
{
f[j] = java.lang.Math.max(f[j],f[j-v[i]]+w[i]);
}
System.out.println(f[V]);
}
}
二.完全揹包問題
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int N,M;//N代表物品個數,M代表揹包的容量
N = scan.nextInt();
M = scan.nextInt();
int f[] = new int[M+1];//f[i]表示在體積爲i的情況下的最大容量
int V,W;//是物品的體積,W是物品的價值
for(int i=1;i<=N;i++)
{
V = scan.nextInt();
W = scan.nextInt();
for(int j=V;j<=M;j++)//這裏遞增以後便可以重複使用
f[j] = java.lang.Math.max(f[j],f[j-V]+W);
}
System.out.println(f[M]);
scan.close();
}
}
三.多重揹包問題
import java.util.Scanner;
public class Main
{
public static void main(String[] args) {
int N,V;
Scanner scanner = new Scanner(System.in);
N = scanner.nextInt();
V = scanner.nextInt();
int v[]=new int[N+1];
int w[]=new int[N+1];
int s[]=new int[N+1];
//初始化條件爲f[0]=0
int f[] = new int[V+1];
for(int i=1;i<=N;i++)
{
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
s[i] = scanner.nextInt();
}
for(int i=1;i<=N;i++)
for(int j=V;j>=0;j--)
for(int k=1;j-k*v[i]>=0&&k<=s[i];k++)
{
f[j]=java.lang.Math.max(f[j],f[j-k*v[i]]+k*w[i]);
}
System.out.println(f[V]);
}
}
下面是多重揹包的二進制優化方法:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class Main
{
public static void main(String[] args) {
int N,V;
List<List<Integer>> lists = new ArrayList<>();
Scanner scanner = new Scanner(System.in);
N = scanner.nextInt();
V = scanner.nextInt();
int v[]=new int[N+1];
int w[]=new int[N+1];
int s[]=new int[N+1];
//初始化條件爲f[0]=0
int f[] = new int[V+1];
for(int i=1;i<=N;i++)
{
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
s[i] = scanner.nextInt();
//二進制分割,下面的代碼可以取s[i]=10驗證分割爲1,2,4,3
for(int k=1;k<=s[i];k=2*k)
{
s[i]-=k;
List<Integer> list = new ArrayList<>();
list.add(k*v[i]);
list.add(k*w[i]);
lists.add(list);
}
if(s[i]>0)
{
List<Integer> list = new ArrayList<>();
list.add(s[i]*v[i]);
list.add(s[i]*w[i]);
lists.add(list);
}
}
//套用01揹包
for(List<Integer> list : lists)
{
for(int j=V;j>=0;j--)
{
if(j-list.get(0)>=0)
f[j]=java.lang.Math.max(f[j],f[j-list.get(0)]+list.get(1));
}
}
System.out.println(f[V]);
}
}
下面是單調隊列優化