完全背包问题
完全背包问题贪心算法入门
题目描述
一个背包容量为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));
}
}
}
思考:
其实可以有多种贪心策略, 比如此题中我可以先做价值大的作业, 也可以先做耗时最小的作业, 这些尽管是局部最优解, 但不是全局最优解. 贪心就是要选出一种策略, 它是局部最优, 最后可以得到全局最优 可以某些局部最优不一定可以到达全局最优. 想想做人有时候也是这样啊, 步步为赢,每一步都顺风顺水, 最终也不一定会得到最好的结果, 年轻人还是多用困难历练以下自己