代码随想录算法训练营Day34 | 860.柠檬水找零、406.根据身高重建队列、452.用最少数量的箭引爆气球

860.柠檬水找零

这题的数值设定地很巧,导致解法也很简单,重点在于对每类钞票进行分解:

20元:10元钞票只能被20元找零消耗,所以遇到20元钞票优先消耗10+5,如果没有10元钞票则消耗三张5元钞票

10元:只能消耗一张5元钞票

5元:不需要任何消耗

bool lemonadeChange(vector<int>& bills) {
	vector<int> money(2, 0);	// 存放5和10元钞票的张树

	for (int i = 0; i < bills.size(); ++i) {
		if (bills[i] == 5)
			++money[0];
		else if (bills[i] == 10) {
			++money[1];
			--money[0];
		}
		else {
			if (money[1] > 0) {
				--money[0];
				--money[1];
			}
			else
				money[0] -= 3;
		}
		if (money[0] < 0)
			return false;
	}
	return true;
}

406.根据身高重建队列

难啊,凭自己的理解对k进行优先排序,写了半天没写出来。实际上应该对身高h进行优先排序。

排序规则:优先对身高进行降序排列,相同身高则k小的排前面。该排序规则能实现:

· 对于一个位置,当前位置之前所有人的身高都大于当前位置人的身高。所以直接将这个人插入到其对应索引k的位置即可。

· 按以上规则插入,如果之后还有人插入到这个人的前面,由于插入者的身高必定比这个人小,所以并不会影响这个人k的正确性(相同身高下k值小的优先插入,所以相同身高不会插入到前面)

class cmp {
public:
	bool operator()(const vector<int>& v1, const vector<int>& v2) {
		if (v1[0] != v2[0]) {
			// 身高高的排前面
			return v1[0] > v2[0];
		}
		else {
			// 身高相同时k小的排前面
			return v1[1] < v2[1];
		}
	}
};


vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
	// 先排个序
	std::sort(people.begin(), people.end(), cmp());

	// 直接逐个插入到索引k的位置
	vector<vector<int>> queue;
	for (int i = 0; i < people.size(); ++i) {
		queue.insert(queue.begin() + people[i][1], people[i]);
	}

	return queue;
}

 对于有多个维度的问题,应该分开考虑每一个维度,最后再对各个维度的结果进行合并或取舍


452.用最少数量的箭引爆气球

这题也应用了排序,重点是理解排序的作用,以及排序后使用右边界来判断是否重叠

排序的目的:使可能发生重叠的气球尽可能聚在一起

排序规则:左边界从小到大排序,左边界相同的情况下右边界从大到小排序(右边界排序规则不重要,重点在于左边界的排序)

排序能实现的效果

如果未排序,对于判断是否重叠需要四次判断:

· 判断左边界是否大于上一个气球的左边界,且小于上一个气球的右边界(2次)

· 判断右边界是否大于上一个气球的左边界,且小于上一个气球的右边界(2次)

· 就算进行的更多的判断,由于可能重叠的气球没有聚在一起,还是可能会浪费箭数

排序后,判断是否重叠仅需要一次判断:只需要判断当前气球的左边界是否小于上一只箭的最大右边界

· 由于左边界经过排序,如果当前气球的左边界不在上一个气球区间内,则右边界一定也不在其区间内,所以只需要判断左边界即可

· 由于左边界经过排序,所以当前左边界必定小于等于上一个气球的左边界,所以只需要判断与上一个气球的右边界相比是否符合条件即可

· 对于用于对比的右边界,不应该直接对比上一个气球的右边界,而应该对比上一只箭的最大右边界(即该只箭所射爆的所有气球中右边界的最小值),超过该值说明需要一只新箭

class cmp {
public:
	// 使用常量引用能大幅缩短运行时间
	bool operator()(const vector<int>& v1, const vector<int>& v2) {
		if (v1[0] != v2[0]) {
			// 左边界小的排在前面
			return v1[0] < v2[0];
		}
		else {
			// 如果左边界相同,则右边界小的排在后面
			return v1[1] > v2[1];
		}
	}
};

int findMinArrowShots(vector<vector<int>>& points) {
	// 先排序使重叠的气球尽量聚在一起
	std::sort(points.begin(), points.end(), cmp());

	int ans = 1;
	// 由于已经对左边界进行了排序,所以只需要判断右边界是否符合条件即可
	// 即下一个气球的左边界不能超过当前这只箭所能达到的最大右边界
	int right = points[0][1];
	for (int i = 1; i < points.size(); ++i) {
		// 超出当前这只箭所能达到的最大右边界时,箭数+1,同时更新新的这只箭的右边界
		if (points[i][0] > right) {
			++ans;
			// 更新新箭的右边界
			right = points[i][1];
		}
		else {
			// 更新当前这只箭的右边界
			right = std::min(right, points[i][1]);
		}
	}
	return ans;
}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值