在做vivo2019提前批笔试时,遇到了——动态规划背包问题
vivo2019提前批笔试第三题:
小v负责一次活动礼品采购,每一款礼品的受欢迎程度(热度值)各不相同,现给出总金额以及各个礼品的单价和热度值,且每个礼品只购买一个,如何购买可以使得所有礼品的总热度值最高。
输入:
第一行是一个正整数,表示总金额(不大于1000)
第二行是一个长度为n的正整数数组,表示礼品单价(n不大于100)
第三行是一个长度为n的正整数数组,表示对应礼品的热度值
输出:
一个正整数,表示可以获得的最高总热度值
样例输入:1000
200 600 100 180 300 450
6 10 3 4 5 8
样例输出:21
由于讲解的方便,把样例输入改为如下:
样例输入:10
2 6 1 4 3 5
6 10 3 4 5 8
样例输出:19
用二维数组0/1背包解决
代码如下:
import java.util.*;
public class vivo_03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// int total = 1000;
// int[] arrA = {200,600,100,180,300,450};
// int[] arrB = {6,10,3,4,5,8};
int total = 10;
int[] arrA = {2,6,1,4,3,5};
int[] arrB = {6,10,3,4,5,8};
System.out.println(solution(total,arrA,arrB));
}
private static int solution(int total, int[] arrA, int[] arrB) {
// TODO Auto-generated method stub
if (total <= 0)
return 0;
int[][] dp = new int[arrA.length][total+1];
//for(int i=0;i<arrA.length;i++) {
// for(int k=0;k<total+1;k++) {
// System.out.print(dp[i][k]+" ");
// }
//System.out.println();
// }
for (int i=0; i<total+1; i++) {
if (i >= arrA[0])
dp[0][i] = arrB[0];
}
for (int i=1; i<arrA.length; i++) {
for (int j=1; j<total+1; j++) {
if (arrA[i] > j) {
dp[i][j] = dp[i-1][j];
} else {
int temp1 = dp[i-1][j-arrA[i]]+arrB[i];
int temp2 = dp[i-1][j];
dp[i][j] = temp1 > temp2 ? temp1 : temp2;
}
}
}
return dp[arrA.length-1][total];
}
}
输出结果:19
讲解:
处理这类问题的方法是
(1)、根据总金额和礼品单价的数组长度,建立一个初始数组表:
int[][] dp = new int[arrA.length][total+1];
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
(2)、先对第一行进行背包处理;
for (int i=0; i<total+1; i++) {
if (i >= arrA[0])
dp[0][i] = arrB[0];
}
0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
(3)、对剩余的行进行背包处理
for (int i=1; i<arrA.length; i++) {
for (int j=1; j<total+1; j++) {
if (arrA[i] > j) {
dp[i][j] = dp[i-1][j];
} else {
int temp1 = dp[i-1][j-arrA[i]]+arrB[i];
int temp2 = dp[i-1][j];
dp[i][j] = temp1 > temp2 ? temp1 : temp2;
}
}
}
0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
0 | 0 | 6 | 6 | 6 | 6 | 10 | 10 | 16 | 16 | 16 |
0 | 3 | 6 | 9 | 9 | 9 | 10 | 13 | 16 | 19 | 19 |
0 | 3 | 6 | 9 | 9 | 9 | 10 | 13 | 16 | 19 | 19 |
0 | 3 | 6 | 9 | 9 | 11 | 14 | 14 | 16 | 19 | 19 |
0 | 3 | 6 | 9 | 9 | 11 | 14 | 14 | 17 | 19 | 19 |
有表可知,最高总热度值为19。
用一维数组0/1背包解决
代码如下:
public static void main(String[] args) {
int total = 10;
int[] arrA = {2,6,1,4,3,5};
int[] arrB = {6,10,3,4,5,8};
System.out.println(solution(total,arrA,arrB));
}
private static int solution(int total, int[] arrA, int[] arrB) {
if (total <= 0)
return 0;
int[] dp = new int[total+1];
Arrays.fill(dp, 0);
for(int j=0;j<arrA.length;j++) {
for(int i=total;i>0;i--) {
if(i>=arrA[j]) {
dp[i] = Math.max(dp[i-arrA[j]]+arrB[j], dp[i]);
}
}
for(int k=0;k<dp.length;k++) {
System.out.print(dp[k]+" ");
}
System.out.println();
}
return dp[total] ==0 ? 0 : dp[total];
}
结果也是19。
先写到这里吧,这里还没有介绍背包问题的原理,后面有时间把原理补充上,就看着更容易理解了