题解 | #购物单#动态规划

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
                    }
                }
            }

这题花的时间挺久的,必须写个题解记录,防止遗忘

全部评论

相关推荐

头像
11-07 01:12
重庆大学 Java
精致的小松鼠人狠话不多:签哪了哥
点赞 评论 收藏
分享
10-06 12:46
门头沟学院 Java
跨考小白:定时任务启动
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务