贪心算法

本文探讨了两个经典算法问题:如何在旅行中实现最少的加油次数,以及如何用最少的箭矢射击多个气球。通过详细解析算法逻辑,包括使用最大堆优化加油策略和对气球坐标进行排序以提高射击效率,本文提供了深入的理解和解决方案。

最少加油次数
汽车经过n个加油站,对于这n个加油站,应该在哪个加油站停下来加油,最终既能到达终点,又使得加油次数最少?
若按顺序遍历加油站,则面临:
如果在某个加油站停下来加油,可能是没有必要,有可能不进行这次加油也能到达终点;如果在某个加油站不停下来加油,可能由于汽油不够而无法到达终点或者后面需要更多次的加油来到达终点。
何时加油最合适?
油用光的时候
在哪个加油站加油最合适
在油量最多的加油站加油最合适
当油用光时选择该位置之前油量最多的一个加油站。

  1. 设置一个最大堆用来存储经过的加油站的汽油量;
  2. 按照起点至终点的方向,遍历各个加油站之间的距离;
  3. 每次需要走两个加油站之间的距离d,如果发现汽油不够走距离d时,从最大堆中取出一个油量添加,直到可以足够走距离d;
  4. 如果把最大堆的汽油都添加仍不够进行距离d,则无法到达终点;
  5. 当前油量p减少d;
  6. 将当前加油站油量添加至最大堆。
public int minRefuelStops(int target, int startFuel, int[][] stations) {
		PriorityQueue<Integer> big=new PriorityQueue<>((a1, a2) -> a2 - a1);
		if(startFuel>=target)
			return 0;
		if(stations==null||stations.length<1||startFuel<stations[0][0])
			return -1;
		int count=0;
		int p=startFuel-stations[0][0];//到达第一个加油站后的剩余汽油量
		big.offer(stations[0][1]);//将第一个加油站的汽油量添加进最大堆
		for(int i=1;i<=stations.length;i++) {//判断是否能够到达第i个加油站
			int d;
			if(i==stations.length)
				d=target-stations[stations.length-1][0];
			else
				d=stations[i][0]-stations[i-1][0];//第i个加油站和第i-1个加油站之间的距离
			while(p<d&&!big.isEmpty()) {
				p+=big.poll();//当前汽油量无法到达第i个加油站 需要加油,并且选择油量最多的加油站
				count++;
			}
			if(p>=d) {//当前汽油量足够到达加油站i
				if(i==stations.length)
					return count;
				big.offer(stations[i][1]);
				p=p-d;
			}else {//经过加油之后还是无法到达加油站i说明无法到达终点
				return -1;
			}
		}
		return count;
    }

射击气球
重叠or不重叠
n个气球互不重叠则需要n个弓箭手。
对于某个气球至少需要使用1只弓箭将它击穿
在这只气球被击穿的同时,尽可能击穿其他更多的气球
思路:

  1. 对于各个气球进行排序,按照气球的左端点递增排序;
  2. 遍历气球数组,同时维护一个射击区间,在满足可以将当前气球射穿的情况下,尽可能击穿更多的气球,每击穿一个新的气球,更新一次射击区间(保证射击区间可以将新气球也击穿)。
  3. 如果新的气球无法被击穿了,则需要增加一名弓箭手,即维护一个新的射击区间(将该气球击穿),随后继续遍历气球数组。
public int findMinArrowShots2(int[][] points) {
		if(points==null||points.length<1)
			return 0;
		int count=1;
		Arrays.sort(points,Comparator.comparingInt(a1->a1[0]));
		int end=points[0][1];
		for(int i=1;i<points.length;i++) {
			if(points[i][0]>end) {
				count++;
				end=points[i][1];
			}else {
				continue;
			}
		}
		return count;
	}

跳跃游戏
如果提早跳跃,可能会增加跳跃次数;
如果在位置i前都未跳跃,则可能无法到达位置i后的地方。
在到达某点前若一直不跳跃,发现从该点不能跳到更远的地方了,在这之前肯定有次必要的跳跃。
结论:在无法到达更远的地方时,在这之前应该跳到一个可以到达更远位置的位置。

  1. 设置current为当前可达到的最远位置;
  2. 设置pre为在遍历各个位置的过程中,各个位置可达到的最远位置;
  3. 设置count为最少跳跃次数;
  4. 利用i遍历nums数组,若i超过current,则count加1,current=pre;
  5. 遍历过程中若nums[i]+i更大,则更新pre=nums[i]+i。
public int jump1(int[] nums) {
		if(nums==null||nums.length<2)
			return 0;
		int current=nums[0];
		int pre=nums[0];
		int count=1;
		for(int i=1;i<nums.length;i++) {
			if(i>current) {
				current=pre;
				count++;
			}
			pre=Math.max(i+nums[i], pre);
		}
		return count;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值