题解 | #购物单#动态规划
1、思路分析
如果这题把附件去掉,只考虑主件是不是跟01背包问题完全一样?。所以我也是想着怎么把附件给忽略掉。
最终的想法就是把主件和附件合在一起,它就是一个物品,只是这个物品会有不同的价格与满意度。
这个物品可能存在以下四个状态:
1、只有主件
2、主件+附件1
3、主件+附件2
4、主件+附件1+附件2
也就是说这是一个有不同状态的物品的背包问题。
2、解法
解法肯定也是在背包问题的基础上,不过在处理的时候需要增加一个状态的判断,对物品的四个状态都要处理,取其中满意度最大的即可。
整体三步:处理输入,合并主件附件,利用动态规划解题。具体的编码流程如下:
1、处理输入
while (in.hasNextInt()) {
int money = in.nextInt();
int m = in.nextInt();
int[] v = new int[m + 1]; //价值
int[] p = new int[m + 1]; //重要度(临时算一下满意度,后面没用了)
int[] q = new int[m + 1]; //是否附件
int[] w = new int[m + 1]; //满意度
for (int i = 1; i <= m; i++) {
v[i] = in.nextInt();
p[i] = in.nextInt();
q[i] = in.nextInt();
w[i] = v[i] * p[i];
}
//上面是处理输入,不是重点....................................
}
2、把主件和附件合并
合并的方式有很多种,可以搞一个内部类,也可以用数组、列表这些,方便处理即可。我这里我是用的map和list。
goods:物品价值,key为物品编号,value为其几种状态的价值
weights:物品满意度,key为物品编号,value为其几种状态对应的满意度
因为主件和附件的顺序不知道,我只能遍历了两次,第一次把主件放进去了,第二次在主件的基础上添加附件同时更新各个状态的价值和满意度。
Map<Integer,List<Integer>> goods = new HashMap<>(); //物品价值,key为物品编号,value为其几种状态的价值
Map<Integer,List<Integer>> weights = new HashMap<>(); //物品满意度,key为物品编号,value为其几种状态的满意度
for (int i = 1; i <= m; i++) {
if (q[i] == 0) {
//先放入主件
List<Integer> good = new ArrayList<>();
good.add(v[i]);
goods.put(i,good);
List<Integer> weight = new ArrayList<>();
weight.add(w[i]);
weights.put(i,weight);
}
}
for (int i = 1; i <= m; i++) {
if ( q[i] != 0) {
//再放附件,把主件对应的列表list拿出来去更新那几种状态
List<Integer> good = goods.get(q[i]);
List<Integer> weight = weights.get(q[i]);
int len = good.size();
for (int j = 0; j < len; j++) {
//更新价格和满意度
good.add(good.get(j) + v[i]);
weight.add(weight.get(j) + w[i]);
}
}
}
3、解题(在背包问题的基础上加一个循环,遍历物品的几种状态,去最大值)
1、首先看看背包问题的解法代码
//定义状态 dp[money] 表示钱为money时可以获得的最大满意度
int[] dp = new int[money+1];
//依次对每个物品进行处理
for (int i = 1; i <= m; i++) {
for (int j = money; j >= v[i]; j-=10) { //注意题目中说的都是10的倍数
dp[j] = Math.max( dp[j-v[i] + w[i] , dp[j])
}
}
}
2、在其循环体,也就是状态转移过程中,增加一个遍历,取到最大值就行了。下面只对增加的那部分代码写注释。
int[] dp = new int[money+1];
for (int i = 1; i <= m; i++) {
if (q[i] == 0){//只处理主件
for (int j = money; j >= v[i]; j-=10) {
List<Integer> good = goods.get(i); //获取物品i不同状态的价值
List<Integer> weight = weights.get(i); //同上,拿的是满意度
int max = dp[j]; //记录几种状态下的最大值
for (int k = 0; k < good.size(); k++) { //遍历物品的不同状态
Integer v_ = good.get(k); //当前状态物品的价格
Integer w_ = weight.get(k);//当前状态物品的满意度
if ( j >= v_){
max = Math.max(Math.max(dp[j - v_] + w_,dp[j]),max);
}
}
dp[j] = max; //当前money = j时,能获得的最大满意度是max
}
}
}
这题花的时间挺久的,必须写个题解记录,防止遗忘


查看20道真题和解析