文章目录
- 采药(01背包)
- 装箱问题(01背包)
- 宠物小精灵之收服(二维费用01背包👍😘)
- 数字组合(01背包)
- 买书(完全背包)
- 货币系统(完全背包)
采药(01背包)
✍
典型01背包模板题
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = (int) (2e5 + 10);
static int[][] dp = new int[110][1100]; //最长不上升子序列
static int k = 0, n = 0;
static int[] t = new int[N];
static int[] val = new int[N];
static int cnt = 0;
static int time = 0;
public static void main(String[] args) throws Exception{
String[] tm = br.readLine().split(" ");
time = Integer.parseInt(tm[0]);
n = Integer.parseInt(tm[1]);
for(int i = 1; i <= n; i++) {
String[] tv = br.readLine().split(" ");
t[i] = Integer.parseInt(tv[0]);
val[i] = Integer.parseInt(tv[1]);
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= time; j++) {
if(t[i] > j) {
dp[i][j] = dp[i - 1][j];
}else {
dp[i][j] = Math.max(dp[i - 1][j],dp[i - 1][j - t[i]] +val[i]);
}
}
}
System.out.println(dp[n][time]);
}
}
装箱问题(01背包)
✍
本题可以从两个方向去思考:
法一:
给定n个物品,及每个物品对应的体积,要让剩余空间最少,就是求放进去的体积的体积的最大值。我们将每个物品的体积当作背包的价值,不就等价于之前的求背包的最大价值了。
最后用总的体积-dp[n][vv]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[100][21000];
static int[] v = new int[21000];
static int n = 0,vv = 0;
static long ans = 0;
public static void main(String[] args) throws Exception{
vv = Integer.parseInt(br.readLine());
n = Integer.parseInt(br.readLine());
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
//
// for(int j = 0; j <= vv; j++) {
// dp[0][j] = j;
// }
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
dp[i][j] = dp[i - 1][j];
if(j >= v[i]) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v[i]] + v[i]);
}
}
}
System.out.println(vv - dp[n][vv]);
}
}
法2:
直接设dp[i][j]代表前i个物品,体积为j的最少剩余空间。
初始化dp[0][j]代表前0件物品,体积为j,那么它的最少剩余空间就是j,初始化这些状态。
然后进行转移
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[100][21000];
static int[] v = new int[21000];
static int n = 0,vv = 0;
static long ans = 0;
public static void main(String[] args) throws Exception{
vv = Integer.parseInt(br.readLine());
n = Integer.parseInt(br.readLine());
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
for(int j = 0; j <= vv; j++) {
dp[0][j] = j;
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
dp[i][j] = dp[i - 1][j];
if(j >= v[i]) {
dp[i][j] = Math.min(dp[i][j],dp[i - 1][j - v[i]]);
}
}
}
System.out.println(dp[n][vv]);
}
}
宠物小精灵之收服(二维费用01背包👍😘)
✍
本题是01背包的拓展版-二维费用背包
与平时的01背包(一维费用背包的不同是多了一维代价)
一维费用背包我们一般只考虑背包体积是否放的下。
二维费用背包是:同时考虑了背包容积与背包承重。
针对于这个题目:
本题的两个花费分别是:
精灵球的数量和皮卡丘体力值
而获得的价值就是野生小精灵的数量,需要注意的是,匹拉丘的体力值不能为0,所以皮卡丘的体力值的范围为0-m-1,最后在找一个消耗体力值最小的即可。
需要注意的是,本题必须使用滚动数组优化掉一层,否则会MLE。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[1100][510];
static int[] v = new int[110]; //伤害
static int[] w = new int[110]; //数量
static int n = 0,m = 0,k = 0,num = 0;
static long ans = 0;
//dp[i][j][k]只考虑前i个,精灵球数量不超过j,皮卡丘体力值不超过k的最大值
public static void main(String[] args) throws Exception{
String[] nmk = br.readLine().split(" ");
n = Integer.parseInt(nmk[0]); //精灵球数量
m = Integer.parseInt(nmk[1]); //皮卡丘体力值
num = Integer.parseInt(nmk[2]); //野生小精灵的数量
for(int i = 1; i <= num; i++) {
String[] wv = br.readLine().split(" ");
w[i] = Integer.parseInt(wv[0]);
v[i] = Integer.parseInt(wv[1]);
}
for(int i = 1; i <= num; i++) {
for(int j = n; j >= w[i]; j --) {
for(int k = m - 1; k >= v[i]; k--) {
dp[j][k] = Math.max(dp[j][k],dp[j - w[i]][k - v[i]] + 1);
}
}
}
int s = m;
while(s > 0 && dp[n][m-1] == dp[n][s - 1]) {
s--;
}
System.out.println(dp[n][m - 1] + " " + (m - s));
}
}
数字组合(01背包)
首相可以想到枚举所有数字的选不选的情况,然后判断是否可行,但是肯定是过不了的,时间复杂度太高了。
每个数字都有选不选,那不就和每个物品都可以放入或者不放入背包差不多嘛,所以可以采用01背包进行求解。
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 110;
static int M = 11000;
static int[][] dp = new int[N][M];
//前i个数 构成m的方案数
static int n = 0, m = 0;
static int[] a= new int[N];
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
String[] aa = br.readLine().split(" ");
for(int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(aa[i - 1]);
}
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++) {
dp[i][j] += dp[i - 1][j];
if(a[i] <= j) {
dp[i][j] += dp[i - 1][j - a[i]];
}
}
}
System.out.println(dp[n][m]);
}
}
买书(完全背包)
✍
因为每本书可以买多本,不需要考虑书本数量的限制,我们只需要考虑钱就行,所以是完全背包。
❗:一定要注意v那一维一定要从0开始,要不会导致有些状态转换不过来。
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 1100;
static int[] v = {0,10,20,50,100};
static int[][] dp = new int[5][M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
dp[0][0] = 1;
for(int i = 1; i <= 4; i++) { //书
for(int j = 0; j <= n; j++) { //money
for(int k = 0; k <= j / v[i]; k++) { //num
dp[i][j] += dp[i - 1][j - v[i]*k];
}
}
}
System.out.println(dp[4][n]);
}
}
优化版本完全背包:
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 1100;
static int[] v = {0,10,20,50,100};
static int[] dp = new int[M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
dp[0] = 1;
for(int i = 1; i <= 4; i++) { //书
for(int j = v[i]; j <= n; j++) { //money
dp[j] += dp[j - v[i]];
}
}
System.out.println(dp[n]);
}
}
货币系统(完全背包)
和上一题基本相同,需要注意数据范围,我们需要开long
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 3100;
static int[] v = new int[20];
static long[] dp = new long[M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = v[i]; j <= m; j++) {
dp[j] += dp[j - v[i]];
}
}
System.out.println(dp[m]);
}
}