Java算法练习day5
一、小红的口罩
‘
使用最小堆(PriorityQueue)模拟贪心
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
long k = in.nextLong(); // 注意使用 long,防止溢出
// 最小堆,存储当前所有口罩的不舒适度
PriorityQueue<Long> heap = new PriorityQueue<>();
for (int i = 0; i < n; i++) {
long x = in.nextLong();
heap.offer(x);
}
long sum = 0; // 总不舒适度
int days = 0; // 天数
while (true) {
long cur = heap.poll(); // 取当前不舒适度最小的口罩
if (sum + cur > k) {
break; // 超过阈值,无法使用
}
sum += cur;
days++;
heap.offer(cur * 2); // 使用后翻倍,重新入堆
}
System.out.println(days);
}
}
核心思路:贪心 + 最小堆
每天我们要选一个口罩来戴。显然,为了在总成本不超过 k 的前提下撑尽量多天数,每次应该选当前不舒适度最小的口罩。
因为这样做,今天付出的成本最小,剩下的“预算”就最多,从而可能获得更多天数。
翻倍机制意味着:一个口罩今天用了,明天它的成本就变大了,所以不能再无脑重复用同一个,它可能不再是明天最便宜的选择。因此我们需要动态维护所有口罩的当前成本,并且每次取出最小的。
这正好可以用最小堆(PriorityQueue) 来实现:堆顶永远是当前所有口罩中不舒适度最小的那个。
.1 数据结构:PriorityQueue<Long>
-
Java 默认是最小堆,
heap.poll()返回并删除堆顶(最小值)。 -
为什么用
Long?因为口罩翻倍可能很快超过int范围(例如a[i]=10^5,翻倍几次就超了),而k也可能很大,所以统一用long安全。
3.2 主循环的逻辑
-
每次循环,假设我们选择当前最便宜的口罩使用。
-
检查条件:
sum + cur <= k才允许用。-
如果成立:更新总成本、天数,然后把翻倍后的口罩放回堆。
-
如果不成立:连最便宜的都不能用了,那其他口罩更贵,更不能用,所以直接退出循环。
-
二、春游
分析:
-
如果n<=2,直接输出min(a,b)(因为可以租一条双人船或三人船,但三人船可能更便宜)。
-
否则,如果3*a <= 2*b,意味着双人船人均成本更低(因为双人船人均a/2,三人船人均b/3,3*a <= 2*b等价于a/2 <= b/3)。所以优先用双人船。
-
ret = (n/2)*a
-
如果n是奇数,剩一个人:
方案1:多租一个双人船(但只坐一人)花费a,或者多租一个三人船花费b -> min(a,b)
方案2:也可以考虑减少一条双人船(-a),然后租一条三人船(+b),即用b替代两个a?注意:剩一个人时,可以有两种思路:-
直接加一个船:min(a,b)
-
或者退掉一条双人船(省a),然后改租一条三人船(花b),这样原来两个人加剩下一个人共三人坐一条三人船,总花费 ret - a + b
取这两种的最小值。
-
-
-
否则(即3a > 2b),三人船人均成本更低,优先用三人船。
-
ret = (n/3)*b
-
余数情况:
-
余1:剩一个人。
方案1:加一个船 min(a,b)
方案2:退掉一条三人船(-b),然后加两条双人船(+2*a),即用2a替代b来坐原来3人+剩余1人共4人?实际上逻辑是:少用一个三人船(省b),然后多租两个双人船(花2a),这样总花费 ret - b + 2*a -
余2:剩两个人。
方案1:加一个船 min(a,b)
方案2:退掉一条三人船(-b),然后加三条双人船(+3*a),即用3a替代b来坐原来3人+剩余2人共5人?类似。
-
-
注意:还需要考虑余数为0直接输出。
-
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while (T-- > 0) {
long n = sc.nextLong();
long a = sc.nextLong();
long b = sc.nextLong();
long ret = 0;
if (n <= 2) {
// 人数不超过2时,直接选最便宜的船(双人船或三人船)
ret = Math.min(a, b);
} else if (3 * a <= 2 * b) {
// 双人船人均成本更低,优先用双人船
ret = (n / 2) * a;
if (n % 2 == 1) {
// 剩余1人:要么加一个船,要么退一条双人船换一条三人船
long opt1 = ret + Math.min(a, b);
long opt2 = ret - a + b;
ret = Math.min(opt1, opt2);
}
} else {
// 三人船人均成本更低,优先用三人船
ret = (n / 3) * b;
long r = n % 3;
if (r == 1) {
// 剩余1人:加一个船 或 退一条三人船换两条双人船
long opt1 = ret + Math.min(a, b);
long opt2 = ret - b + 2 * a;
ret = Math.min(opt1, opt2);
} else if (r == 2) {
// 剩余2人:加一个船 或 退一条三人船换三条双人船
long opt1 = ret + Math.min(a, b);
long opt2 = ret - b + 3 * a;
ret = Math.min(opt1, opt2);
}
}
System.out.println(ret);
}
sc.close();
}
}
更多推荐
‘




所有评论(0)