背包问题
01背包问题(每个物体最多选一个)
所谓的状态就是一个未知数。状态表示考虑用几维来表示问题。
状态表示从两个方面来考虑,表示的集合是什么,f(i,j)表示集合的属性是什么。每一个状态表示的是一个集合,就是搞清楚f()表示的是那一个集合,比如说背包问题就是所有选法的集合。因为f(i,j)是一个数,表示的是一个集合,数就是这个这个集合的某种属性
属性一般有max,min,数量。背包问题就是最大值。集合表示的一堆选法,所有选法的一个集合。选法满足两个条件,第一个:只从前i个物品当中选。第二个:选出来的物品的总体积小于等于J。f()表示的就是所有满足条件选法的集合。f()存的数是集合每个选法总价值的最大值。
状态计算就是如何一步一步的把每个状态算出来————集合的划分
就是考虑把当前的集合划分为若干个更小的子集,使其能算出来。划分原则:不重(某一个元素不可以属于两个集合)(求最大值可以重复),不漏(某一个集合元素不属于任何集合)。
然后取两部分的最大值就好。
dp问题的优化是对代码,计算方程进行等价变形
public class Main{
public static void main(String[] args) throws Exception {
// 读入数据的代码
Scanner reader = new Scanner(System.in);
// 物品的数量为N
int N = reader.nextInt();
// 背包的容量为V
int V = reader.nextInt();
// 一个长度为N的数组,第i个元素表示第i个物品的体积;
int[] v = new int[N + 1] ;
// 一个长度为N的数组,第i个元素表示第i个物品的价值;
int[] w = new int[N + 1] ;
for (int i=1 ; i <= N ; i++){
// 接下来有 N 行,每行有两个整数:v[i],w[i],用空格隔开,分别表示第i件物品的体积和价值
v[i] = reader.nextInt();
w[i] = reader.nextInt();
}
reader.close() ;
// 正式工作的代码
/*
定义一个二阶矩阵dp[N+1][V+1],
这里之所以要N+1和V+1,是因为第0行表示只能选择第0个物品的时候,即没有物品的时候
第0列表示背包的体积为0的时候,即不能装任何东西的时候
dp[i][j]表示在 只能选择前i个物品,背包容量为j的情况下,背包中物品的最大价值
对于dp[i][j]有两种情况:
1. 不选择当前的第i件物品/第i件物品比背包容量要大,则dp[i][j] = dp[i-1][j]
2. 选择当前的第i件物品(潜在要求第i件物品体积小于等于背包总容量),则能装入的物品最大价值为:
当前物品的价值 加上 背包剩余容量在只能选前i-1件物品的情况下的最大价值
dp[i][j] = dp[i-1][j-v[i]] + w[i]
dp[i][j]在两种情况中选择比较大的情况作为当前的最优解;
即:
if(j >= v[i]):
dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]] + w[i])
else:
dp[i][j] = dp[i-1][j]
*/
int[][] dp = new int[N+1][V+1];
for(int j = 0; j <= V; j++){
dp[0][j] = 0;}
for(int i = 1; i <= N; i++){
for(int j = 0; j <= V; j++){
if(j >= v[i]){
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-v[i]] + w[i]);
}else{
dp[i][j] = dp[i-1][j];
}
}
}
System.out.println(dp[N][V]);
}
}