网上商城优惠活动(一)
以下内容由题目介绍,输入和输出,用例介绍,代码实现4部分组成。 建议理解解题过程,请勿完全死记硬背代码,不断练习,才能提高编码能力。
题目介绍
【背景】
某网上商场举办优惠活动,发布了满减、打折、无门槛3种优惠券,分别为:
- 每满100元优惠10元,无使用数限制,如100199元可以使用1张减10元,200299可使用2张减20元,以此类推;
- 92折券,1次限使用1张,如100元,则优惠后为92元;
- 无门槛5元优惠券,无使用数限制,直接减5元。
【优惠券使用限制】
- 每次最多使用2种优惠券,2种优惠可以叠加(优惠叠加时以优惠后的价格计算),以购物200元为例,可以先用92折券优惠到184元,再用1张满减券优惠10元,最终价格是174元,也可以用满减券2张优惠20元为180元,再使用92折券优惠到165(165.6向下取整),不同使用顺序的优惠价格不同,以最优惠价格为准。在一次购物种,同一类型优惠券使用多张时必须一次性使用,不能分多次拆开使用(不允许先使用1张满减券,再用打折券,再使用一张满减券)。
【问题】
请设计实现一种解决方法,帮助购物者以最少的优惠券获得最优的优惠价格。优惠后价格越低越好,同等优惠价格,使用的优惠券越少越好,可以允许某次购物不使用优惠券。
【约定】
优惠活动每人只能参加一次,每个人的优惠券种类和数量是一样的。
输入和输出
【输入描述】
第一行:每个人拥有的优惠券数量(数量取值范围为[0,10),按满减、打折、无门槛的顺序输入。
第二行:表示购物的人数n(1<=n<=10000)
。
最后n行:每一行表示某个人优惠前的购物总价格(价格取值范围(0,1000,都为整数)。
约定:输入都是符合题目设定的要求的。
【输出描述】
每行输出每个人每次购物优惠后的最低价格以及使用的优惠券总数量,每行的输出顺序和输入的顺序保
持一致。
补充说明:
1.优惠券数量都为整数,取值范围为[0,10]。
2.购物人数为整数,取值范围为[1,10000]。
3.优惠券的购物总价为整数,取值范围为(0,1000]。
4.优惠后价格如果是小数,则向下取整,输出都为整数。
用例介绍
示例1
【输入】
3 2 5
3
100
200
400
【输出】
65 6
155 7
338 4
说明:
【输入说明】
第一行:3种优惠券数量分别为满减券3张,打折券2张,无门槛5张
第二行:总共3个人购物
第三行:第一个人购物优惠前价格为100元
第四行:第二个人购物优惠前价格为200元
第五行:第三个人购物优惠前价格为400元
【输出说明】
输入3个人,输出3行结果,同输入的顺序,对应每个人的优惠结果,如下:
第一行:先使用1张满减券优惠到90元,再使用5张无门槛券优惠25元,最终价格是65元,总共使用6张优惠券。
第二行:先使用2张满减券优惠到180元,再使用5张无门槛券优惠25元,最终价格是155元,总共使用7张优惠券。
第三行:先使用1张92折券优惠到368元,再使用3张满减券优惠30元,最终价格是338元,总共使用4张优惠券。
代码实现
解题思路:
下面代码实现的是一个贪心算法,用于解决一类背包问题,具体来说,它是针对以下场景:
你手头有三种不同类型的折扣券(ticket1、ticket2、ticket3),每个折扣券有一定数量,分别为(tickets[0]、tickets[1]、tickets[2]);
你可以使用这些折扣券在商店里购买任意多个商品,商品的价格分别为
amounts[0]、amounts[1]、…、amounts[n-1],你需要在不超过商品价格总额的前提下,使用尽量少的折扣券。在这个场景下,上面的代码首先读入了折扣券的数量和商品价格列表,然后对于每个商品,通过调用 calculateTicket
函数进行计算,这个函数内部实现了一个贪心算法:对于当前商品,我们分别尝试使用三种折扣券进行折扣,并记录使用每种折扣券的情况(即优惠金额和使用的张数);
首先考虑不使用任何折扣券的情况,计算出使用 ticket2 和 ticket3 两种折扣券的最优方案,并记录下来; 然后考虑使用 ticket1 折扣券的情况,计算出使用
ticket2 和 ticket3 两种折扣券的最优方案,并记录下来; 比较两种情况,选择最优解,输出优惠金额和使用的折扣券数量。
import java.util.Scanner;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int ticket1 = sc.nextInt();
int ticket2 = sc.nextInt();
int ticket3 = sc.nextInt();
int n = sc.nextInt();
int[] amounts = IntStream.range(0, n).map(i -> sc.nextInt()).toArray();
calculateTicket(ticket1, ticket2, ticket3, amounts);
sc.close();
}
private static void calculateTicket(int ticket1, int ticket2, int ticket3, int[] amounts) {
for (int amount : amounts) {
int[] res1 = useTicket1(ticket1, new int[]{amount, 0});
int[] res2 = useTicket2(ticket2, new int[]{amount, 0});
int[] res3 = useTicket3(ticket3, new int[]{amount, 0});
int[] result;
if (amount < 100) {
if (amount <= 62 && ticket3 > 0) {
result = useTicket2(ticket2, res3);
} else {
result = useTicket3(ticket3, res2);
}
System.out.println(result[0] + " " + result[1]);
continue;
}
if (res1[0] < res2[0]) {
int[] copyRes = res1.clone();
int[] r3 = useTicket3(ticket3, res1);
int[] r2 = useTicket2(ticket2, copyRes);
result = r3[0] < r2[0] ? r3 : r2;
} else {
int[] copyRes = res2.clone();
int[] r3 = useTicket3(ticket3, res2);
int[] r1 = useTicket1(ticket1, copyRes);
result = r3[0] < r1[0] ? r3 : r1;
}
System.out.println(result[0] + " " + result[1]);
}
}
private static int[] useTicket1(int ticket, int[] res) {
int tmp = res[0];
while (tmp >= 100 && ticket > 0) {
res[0] -= 10;
ticket--;
res[1]++;
tmp -= 100;
}
return res;
}
private static int[] useTicket2(int ticket, int[] res) {
if (ticket > 0) {
res[0] = (int) Math.floor(res[0] * 0.92);
res[1]++;
}
return res;
}
private static int[] useTicket3(int ticket, int[] res) {
while (!(res[0] < 5 || ticket <= 0)) {
res[0] -= 5;
res[1]++;
ticket--;
}
return res;
}
}