【蓝桥杯3539】买二赠一(思维&贪心&队列&java)

问题描述

 

 

输入输出

 

 

解题思路

涉及到数的大小的话,那肯定是先从小到大排序一下。

然后根据题目的样例说明,看上去像排序后两两取右边?

当然不是,蓝桥杯经典不当人样例误导。 

 想出以下例子,即在右边放一些特别大的值。

显而易见,因为值过大,所以毫无疑问肯定是要选两个200并免费取走80的。

接下来能免费拿的值是2,因为4和5都大于等于2的两倍。

接下来1、1、2无法买二赠一,只能全部买下。

根据上图我们可以推测出一种贪心解法:首先排序,然后从右边开始往左边遍历,若一个数找到两个大于其两倍的数就说明可以配对。

再用这种解法看一下题目样例,发现是适用的。

 

 

代码尝试一(过70%)

import java.util.Arrays;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		int[] zu = new int[n];

		for (int i = 0; i < n; i++) {
			zu[i] = scan.nextInt();
		}

//		最终结果
		int ans = 0;

//		根据个数进行分类
		if (n == 1) {
			ans = zu[0];
		} else if (n == 2) {
			ans = zu[0] + zu[1];
		} else {
//			从小到大排序
			Arrays.sort(zu);

//			从倒数第三个开始判断
			for (int i = n - 3; i >= 0; i--) {
				int num = 0;
				int[] big = new int[2]; // 存储找到的两个大值
				for (int j = n - 1; j > i; j--) {

//					判断是否已被购买
					if (zu[j] == -1) {
						continue;
					}

//					判断是否大于等于当前值的两倍
					if (zu[j] >= zu[i] * 2) {
						big[num++] = j;
					}

//					若找到两个数则结束寻找
					if (num == 2) {
						break;
					}
				}

//				若找到两个大值则更新ans,并将这三个数赋值-1标记为已购买
				if (num == 2) {
//					System.out.println(zu[i] + " " + zu[big[0]] + " " + zu[big[1]]);
					ans += zu[big[0]] + zu[big[1]];
					zu[i] = zu[big[0]] = zu[big[1]] = -1;
				}
			}

//			再遍历一遍购买剩余商品
			for (int i = 0; i < n; i++) {
				if (zu[i] != -1) {
					ans += zu[i];
				}
			}
		}

		System.out.println(ans);

	}
}

过了70%数据,后面30%报超时,说明思路没有错,就是需要优化一下时间复杂度。

Arrays.sort的复杂度是O(n log n),主循环的时间复杂度是O(n^2),可见需要优化主循环。

经过观察可知,取的两个大值都是最右边的两个,因此我们可以将内层循环改为队列,这样就将时间复杂度优化到O(n)了。

同时注意到数据的最大值为10^9,超过了int的最大值,因此将数据类型由int改为long。

 

 

AC代码

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		long[] zu = new long[n];

		for (int i = 0; i < n; i++) {
			zu[i] = scan.nextInt();
		}

//		最终结果
		long ans = 0;

//		根据个数进行分类
		if (n == 1) {
			ans = zu[0];
		} else if (n == 2) {
			ans = zu[0] + zu[1];
		} else {
//			从小到大排序
			Arrays.sort(zu);

			Queue<Integer> queue = new LinkedList<>();
			int max1 = n - 1, max2 = n - 2; // 弹出两个最大值的下标
			boolean used = false; // 记录两个最大值是否被使用,若被使用则需要重新弹出

//			从倒数第三个开始判断
			for (int i = n - 3; i >= 0; i--) {
//				若弹出的两个最大值已被使用,则需要重新弹出
				if (used) {
//					若队列中小于两个值,显然无法凑成3个数进行配对,因此把当前值加入队列
					if (queue.size() < 2) {
						queue.add(i);
						continue;
					} else {
						max1 = queue.poll();
						max2 = queue.poll();
						used = false;
					}
				}

//				判断当前三个数是否满足配对条件
				if (zu[max1] >= 2 * zu[i] && zu[max2] >= 2 * zu[i]) {
					ans += zu[max1] + zu[max2];
					used = true;
				} else {
					queue.add(i);
				}
			}

//			若弹出的两个数未被使用则也需要购买
			if (!used) {
				ans += zu[max1] + zu[max2];
			}

//			队列中的剩余值也需要购买
			while (!queue.isEmpty()) {
				ans += zu[queue.poll()];
			}

		}

		System.out.println(ans);

	}
}

(by 归忆)

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
贪心策略是一种基于贪心思想的算法,它在每个阶段选择局部最优解,最终得到全局最优解。Java语言可以通过以下步骤来生成一个贪心策略: 1. 定义问题的数据结构和输入输出格式。 2. 根据问题的要求,选择一个合适的贪心策略,例如贪心算法贪心选择树等。 3. 实现贪心策略的核心代码,包括选择最优解的条件和更新状态的操作。 4. 编写测试代码,验证贪心策略的正确性和效率。 下面以背包问题为例,演示Java生成一个贪心策略的过程: 1. 定义数据结构和输入输出格式 ```java class Item { int value; int weight; } int knapsack(int W, Item[] items) { // 输入:背包容量 W,物品数组 items // 输出:最大价值 // ... } ``` 2. 选择贪心策略 背包问题可以采用按价值排序的贪心策略:每次选择价值最大的物品放入背包中。 3. 实现贪心策略 ```java int knapsack(int W, Item[] items) { Arrays.sort(items, (a, b) -> b.value - a.value); // 按价值从大到小排序 int max = 0; for (int i = 0; i < items.length && W > 0; i++) { int w = Math.min(W, items[i].weight); // 取当前物品和剩余容量的最小值 max += w * items[i].value / items[i].weight; // 计算当前物品的贡献 W -= w; // 更新剩余容量 } return max; } ``` 4. 编写测试代码 ```java public static void main(String[] args) { Item[] items = { new Item(60, 10), new Item(100, 20), new Item(120, 30) }; int W = 50; int max = knapsack(W, items); System.out.println(max); // 输出220 } ``` 以上就是Java生成一个贪心策略的基本流程。在实际应用中,需要根据具体问题选择合适的贪心策略,并进行适当的优化,以达到更好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

归忆_AC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值