贪心 + 背包问题:可乐

贪心 + 背包问题:可乐

问题:

在这里插入图片描述
在这里插入图片描述

思路:

  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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

happy19991001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值