贪心 + 背包问题:可乐
问题:
思路:
L为我们需要购买的可乐的总体积,我们每进行一次可乐购买,所需要的可乐体积便会减少,设need为我们还需要购买的可乐体积。假设当前性价比最高(即每升单价最低)的饮料为 V 升每瓶, 价格为C。那么我们在不超过need的情况下优先买性价比最高的饮料肯定是最优选择。
故我们优先买need/V瓶该饮料,若此时need%V为0,那么我们刚好买够need升,没有浪费钱也没多买饮料;若此时need%V不等于0 , 那就证明我们还得买,那接下来我们该怎么买呢?
1.我们继续买一瓶V升的饮料,此时等于是我们买的饮料超过了need升,但是选择了当前性价比最高的饮料。
2.我们不买这一瓶V升的饮料,转而去买性价比更低的饮料,因为这么做我们可能可以花更少的钱买够need升饮料。
举个例子: 当前need=10,我们可以买两种可乐,一种可乐的容量为15L,价格为10元,另一种可乐的容量为5L,价格为4元。显然第一种可乐的性价比高,但是我们买两瓶性价比没那么高的饮料花费的钱可以更少。
我们采取的贪心策略是在不超过need的情况下优先买性价比最高的饮料,因此我们需要将饮料按性价比从高到低进行排序(即按每升单价从低到高进行排序)。我们对排序后的饮料进行遍历,每次选择性价比最高的饮料进行购买。因为当购买后还没达到need升时,接下来有两种选择,所以我们需用两个变量保存所花费用。设ans代表一直往后遍历买性价比更低的饮料所需的费用,ans2 代表在 ans基础上再买一瓶当前性价比最高的饮料,这里面所有情况的费用最小值。设当前性价比最高的饮料容量为V升每瓶,若need%V为0,那么我们刚好买够need升,购买过程结束,退出遍历过程。若need%V不等于0,则我们购买need/V瓶该饮料,接着更新need、ans、ans2的值,继续遍历过程。最终输出min(ans, ans2)
即可。
需注意的地方:
1、Java中科学计数法可以表示为诸如1E20形式,不过科学计数法只能表示浮点数,将其转换为long类型会有精度损失,将被解释成另一个数。不过当我们需要表示一个非常大的数,且它具体的值我们不关心,只关心它的数量级够不够大时,也可以用科学计数法进行强制类型转换,将诸如值为1E20的浮点数转化为一个很大的long类型数
2、我们在Arrays.sort()
中提供的实现了Comparator
接口的匿名内部类,其compare
方法在比较两个浮点型数时,千万不要用减法比较,写成下面这种形式:
Arrays.sort(colos, 0, n, new Comparator<Colo>() {
@Override
public int compare(Colo o1, Colo o2) {
return (int)(o1.value - o2.value);
}
});
若0 < o1.value - o2.value < 1
,比如o1.value - o2.value = 0.3
,那么强制类型转换后得到的结果为0,在这种情况下,o1,value > o2.value
,但在排序过程中将不会交换o1,o2的次序,最后会得到和预期相反的结果
在这里多提一点,浮点数的四则运算结果是不精确的,不推荐在Java代码中直接进行浮点数四则运算,如果需要精确的结果,可以使用BigDecimal类
正确的写法为使用比较运算符判断,注意浮点数由于表示精度问题(即二进制不能准确表示浮点数),浮点数之间不能使用==
来判断是否相等:
Arrays.sort(colos, 0, n, new Comparator<Colo>() {
@Override
public int compare(Colo o1, Colo o2) {
if (o1.value > o2.value) {
return 1;
} else {
return -1;
}
}
});
代码:
import java.util.*;
public class Main {
static Colo[] colos = new Colo[32];
public static void main(String[] args) {
int n, i;
long L, c, ans, ans2, num;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
ans = 0;
ans2 = (long)1E20;
n = scanner.nextInt();
L = scanner.nextLong();
for (i = 0;i < n;i++) {
c = scanner.nextLong();
colos[i] = new Colo(c, 1L << i);
}
Arrays.sort(colos, 0, n, new Comparator<Colo>() {
@Override
public int compare(Colo o1, Colo o2) {
if (o1.value > o2.value) {
return 1;
} else {
return -1;
}
}
});
for (i = 0;i < n;i++) {
num = L / colos[i].volume;
ans += num * colos[i].price;
L %= colos[i].volume;
if (L == 0) {
break;
}
ans2 = Math.min(ans + colos[i].price, ans2);
}
System.out.println(Math.min(ans, ans2));
}
}
}
class Colo {
public long price;
public long volume;
public double value;
public Colo (long price, long volume) {
this.price = price;
this.volume = volume;
this.value = (double) price / (double) volume;
}
}