完全背包问题

完全背包问题贪心算法入门

题目描述

一个背包容量为c, 共有n中物品, 第i个物品的重量为Wi, 价值为Vi. 选择物品装入背包, 可以只装入一部分, 请问,如何装,可以使背包的价值最大.

解决思路

此题和0-1背包问题的不同在于, 0-1背包问题要求物品全部装进去, 而完全背包问题要求物品可以只装入一部分,采用贪心的策略解决这个问题

核心策略就是 : 优先选择单价高的物品装入背包

  • 首先计算物品单价
  • 优先选择单价高的物品装入背包
  • 如果物品重量小于背包剩余重量, 接着物品全部装入背包, 重新计算背包剩余重量, 装入下一个物品
  • 如果物品重量大于背包剩余价值, 物品部分装入背包, 此时背包已经满了, 不再装入.

代码思路

// 因为要按照物品的单价排序, 所以可以将物品当作一个类
// 1.自定排序规则, java如何自定排序规则
// 2.java中使用Arrays.sort()或者Collections.sort()进行排序
/**
 * @Author Fizz Pu
 * @Date 2020/10/30 下午3:51
 * @Version 1.0
 * 失之毫厘,缪之千里!
 */

/**
 * 题目描述
 * 临近开学了,大家都忙着收拾行李准  备返校,但 I_Love_C 却不为此担心! 因为他的心思全在暑假作业上:目前为止还未开动。
 * 暑假作业是很多张试卷,我们这些从试卷里爬出来的人都知道,卷子上的题目有选择题、填空题、简答题、证明题等。
 * 而做选择题的好处就在于工作量很少,但又因为选择题题目都普遍很长。如果有 5 张试卷,其中 4 张是选择题,最后一张是填空题,
 * 很明显做最后一张所花的时间要比前 4 张长很多。但如果你只做了选择题,虽然工作量很少,但表面上看起来也已经做了4/5的作业了。
 * I_Love_C决定就用这样的方法来蒙混过关,他统计出了做完每一张试卷所需的时间以及它做完后能得到的价值(按上面的原理,选择题越多价值当然就越高咯)。
 * 现在就请你帮他安排一下,用他仅剩的一点时间来做最有价值的作业。
 
 * 输入
 * 测试数据包括多组。每组测试数据以两个整数 M,N(1<M<20,1<N<10000) 开头,分别表示试卷的数目和 I_Love_C 剩下的时间。
 * 接下来有 M 行,每行包括两个整数 T,V(1<T<N,1<V<10000)分别表示做完这张试卷所需的时间以及做完后能得到的价值,输入以 0 0 结束。
 *
 * 输出
 * 对应每组测试数据输出 I_Love_C 能获得的最大价值。保留小数点 2 位
 *
 * 提示:float 的精度可能不够,你应该使用 double 类型。
 *
 * 样例输入
 * 4 20
 * 4 10
 * 5 22
 * 10 3
 * 1 2
 * 0 0
 * 样例输出
 * 37.00
 */

import java.util.Arrays;
import java.util.Scanner;

/**
 * 此题可以转换为完全背包问题
 * 不是0,1背包问题,原因是作业可以只做一部分
 * N : 可以看成背包总重
 * 试卷价值:物品价值, 试卷所需时间:物品重量
 * 那个卷子单位时间的价值高,我就先做
 */


public class Main {

    static class Node implements Comparable<Node>{
        double price;
        int id; // 在数组中的下标

        public Node(double price, int id)  {
            this.price = price;
            this.id = id;
        }

        @Override
        public int compareTo(Node ob){
            if(ob.price > this.price) return 1;
            else return -1;
        }
    }

    double getMaxValue(double[] times, double[] value, int restTime) {
        int r = restTime; // 背包剩余重量
        double sums = 0;
        // 要通过单价找到对应物品的时间,价值.单个数组排序的方式肯定不行
        // 利用节点记录下信息即可
        Node[] nodes = new Node[times.length];
        for(int i = 0; i < times.length; ++i){
            nodes[i] = new Node(value[i]/times[i], i);
        }

        // 降序排序
        Arrays.sort(nodes);

        for(Node node: nodes){
            int id = node.id;
            if(times[id] <= r){
                sums += value[id];
                r -= times[id];
            } else {
                sums += (r * node.price);
                break;
            }
        }
        return sums;
    }
    
    
    public static void main(String[] args) {
        Main main = new Main();

        // 读取数据
        Scanner scanner = new Scanner(System.in);
        int itemCount, restTime;
        while (true){
            itemCount = scanner.nextInt();
            restTime = scanner.nextInt();
            if(itemCount == 0 && restTime == 0)break;
            double[] times = new double[itemCount];
            double[] values = new double[itemCount];
            for(int i = 0; i < itemCount; ++i){
                times[i] = scanner.nextDouble();
                values[i] = scanner.nextDouble();
            }
            // java中使用System.out.printf()进行格式化输出
            System.out.printf("%.2f\n", main.getMaxValue(times, values, restTime));
        }
    }
}

思考:

其实可以有多种贪心策略, 比如此题中我可以先做价值大的作业, 也可以先做耗时最小的作业, 这些尽管是局部最优解, 但不是全局最优解. 贪心就是要选出一种策略, 它是局部最优, 最后可以得到全局最优 可以某些局部最优不一定可以到达全局最优. 想想做人有时候也是这样啊, 步步为赢,每一步都顺风顺水, 最终也不一定会得到最好的结果, 年轻人还是多用困难历练以下自己

posted @ 2020-11-05 23:11  FizzPu  阅读(135)  评论(0编辑  收藏  举报