0-1 背包问题(模板)
前言:dp问题的题目就是多做题,多总结,之后遇到不同的题的时候与原先做过类似的题比较,然后猜一个状态表示,看一看能不能推出状态转移方程,能推出来就说明状态表示对了,不行就再做出调整。是在不会咋看答案,吸收这里面的经验。
例题1: 01背包_牛客题霸_牛客网
1.题目解析
简而言之就是在n个物品中选出体积总和不大于v的最大重量。
2.算法原理(动态规划 - 线性dp)
1.状态表示
对于这个线性dp问题我们都是以最后一个位置xxxx,然后根据最后一个位置分类讨论。
此题为:设:dp[i][j] 表示在前 i 个物品中挑选出体积不超过j的最大体积。
那么对于i位置的物品,有选和不选两种情况,但选是有条件的,就是i位置的体积必须不大于j。
2.状态转移方程
那么:选 , 需满足条件 arr[i] <= j => dp[i][j] = Math.max(dp[i-1][j-v[i]] + w[i],dp[i-1][j]);
不选 dp[i][j] = dp[i-1][j];
3.初始化
我们多加一行,一列,方便写代码(不用做越界判断,这个很常用,就不过多解释)
对于 i = 0 时,说明0个物品,无论体积多大,初始化都为0;
对于 v = 0 时,说明体积为0,无论物品多少,都不能加入新的物品,初始化都为0;
所以不用初始化。
4.填表(dp这个数组)顺序
从左->右,上-> 下 因为状态表达式当前位置依赖的是左边的位置或左上方的位置。
5.返回值
我们是要求前n个物品中体积不超过v的最大的重量,根据状态表示,可以很容易的得出为dp[n][v]
3.代码
import java.util.*;public class Solution {public int knapsack (int V, int n, int[][] vw) {int[][] dp = new int[n+1][V+1];for(int i = 1;i <= n;i++){for(int j = 1;j <= V;j++){if(j >= vw[i-1][0]){dp[i][j] = Math.max(dp[i-1][j-vw[i-1][0]] + vw[i-1][1],dp[i-1][j]);}else{dp[i][j] = dp[i-1][j];}}}return dp[n][V];}
}
例题2:小红取数_牛客题霸_牛客网(变式)
上面的题如果会了,可以挑战一下这个,两题有很大的相似。
补充知识:
同余定理: 如果 A % k = a , B % k = b 且( A + B ) % k == 0 那么 (a + b) % k = 0。
1.题目解析
在n个数中挑选出 和 是 k的倍数的最大值。
2.算法原理(动态规划)
1.状态表示:(对于这个状态表示我就试了很多种)
设:dp[ i ][ j ] 表示在前 i 个数中选出是k倍数的最大和。
2.状态转移方程
那么对于 i 位置的数,就有选和不选两种情况。
选: 需要 ( dp[ i - 1 ][ tep ] + arr[ i ] ) % k = j , tep = (j - arr[i] % k + k ) % k
dp[ i ][ j ] = Math.max(dp[i-1][tep] + arr[i] , dp[ i - 1 ][ j ]) ,因为要最大所以在两种情况取最大值。
不选: dp[ i ][ j ] = dp[ i - 1 ][ j ] 。
3.初始化
我们这里初始化dp为: long[][] dp = new long[n+1][k];
那么 对于 前 0 的 数,也就没有数嘛,dp[ 0 ][ xx ] = 0;
这样便不用初始化了
4.填表顺序
根据状态方程,当前位置的值 依赖上一行的数,所以我们从左 -> 右 ,上 -> 下;
5.返回值
有状态表示得:dp[n][0]
3.代码
import java.util.*;public class Main{public static void main(String[] args){//处理输入和初始化Scanner sc = new Scanner(System.in);int n = sc.nextInt();int k = sc.nextInt();long[] arr = new long[n];for(int i = 0;i < n;i++){arr[i] = sc.nextLong();}long[][] dp = new long[n+1][k];// 主逻辑for(int i = 1;i <= n;i++){for(int j = 0;j < k;j++){int tep = (int)((j - arr[i-1] % k + k) % k);if((dp[i-1][tep] + arr[i-1]) % k == j){dp[i][j] = Math.max(dp[i-1][j],dp[i-1][tep] + arr[i-1]);}else{dp[i][j] = dp[i-1][j];}}}System.out.println(dp[n][0] == 0 ? -1 : dp[n][0]);}
}
ok, 结束了。 时间匆匆,马上就开学了。