目录
860. 柠檬水找零
题目描述:
输入输出描述:
思路和想法:
406. 根据身高重建队列
题目描述:
输入输出描述:
思路和想法:
452. 用最少数量的箭引爆气球
题目描述:
输入输出描述:
思路和想法:
435. 无重叠区间
题目描述:
输入输出示例:
思路和想法:
763. 划分字母区间
题目描述:
输入输出描述:
思路和想法:
56. 合并区间
题目描述:
输入输出描述:
思路和想法:
738. 单调递增的数字
题目描述:
输入输出描述:
思路和想法:
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
示例 1:
输入:bills = [5,5,5,10,20] 输出:true
解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 由于所有客户都得到了正确的找零,所以我们输出 true。
示例 2:
输入:bills = [5,5,10,10,20] 输出:false
解释: 前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 由于不是每位顾客都得到了正确的找零,所以答案是 false。
提示:
只需要记录五美元和十美元个数,遇到20美元时,两种找零方式:①3张5美元 ②一张五美元和一张十美元。
一张五美元和一张十美元这种找零方式---局部优先。
#include
using namespace std;
bool lemonadeChange(vector& bills) {
int Fivecount = 0;
int Tencount = 0;
for (int i = 0; i < bills.size(); i++)
{
if(bills[i] == 5){
Fivecount++;
}
else if(bills[i] == 10){
Fivecount--;
Tencount++;
}
else if(bills[i] == 20){
if(Tencount == 0){
Fivecount = Fivecount -3;
}
if(Tencount > 0){
Fivecount--;
Tencount--;
}
}
if(Fivecount < 0 || Tencount < 0){
return false;
}
}
return true;
}
int main(void){
int num;
vector bills;
while (cin >> num) {
bills.push_back(num);
if (::getchar() == '\n') {
break;
}
}
bool result;
result = lemonadeChange(bills);
if(result == 1){
cout << "true" << endl;
} else{
cout << "false" << endl;
}
return 0;
}
/*
示例1:
5 5 5 10 20
示例2:
5 5 10 10 20
* */
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
示例 1:
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
示例 2:
输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]] 输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
提示:
核心思路在二维vector的排序规则上,身高从大到小排(身高相同k小的站前面)。
#include
using namespace std;
static bool cmp(const vector& a, const vector& b) {
if (a[0] == b[0]) return a[1] < b[1]; //0,1代表列,当身高相等时,按k升序排列
return a[0] > b[0]; //按身高降序排列
}
vector> reconstructQueue(vector>& people) {
sort(people.begin(), people.end(), cmp);
list> que; //list底层是链表实现
for (int i = 0; i < people.size(); i++)
{
int position = people[i][1]; //对应的Ki
list>::iterator it = que.begin(); //初始位置
while(position--){ //寻找插入位置
it++;
}
que.insert(it,people[i]);
}
return vector>(que.begin(), que.end());
}
int main(void){
int a;//行数
int b;//列数
cin >> a >> b;
vector> people(a,vector(b));
for (int i = 0; i < a; ++i) {
for (int j = 0; j < b; ++j) {
cin >> people[i][j];
}
}
vector> result;
result = reconstructQueue(people);
for (int i = 0; i < a; ++i) {
for (int j = 0; j < b; ++j) {
cout << result[i][j] << " ";
}
cout << endl;
}
return 0;
}
/*
示例1:
6
2
7 0
4 4
7 1
5 0
6 1
5 2
示例2:
6
2
6 0
5 0
4 0
3 2
2 2
1 4
* */
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
示例 1:
输入:points = [[10,16],[2,8],[1,6],[7,12]] 输出:2
解释:气球可以用2支箭来爆破: -在x = 6处射出箭,击破气球[2,8]和[1,6]。 -在x = 11处发射箭,击破气球[10,16]和[7,12]。
示例 2:
输入:points = [[1,2],[3,4],[5,6],[7,8]] 输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。
示例 3:
输入:points = [[1,2],[2,3],[3,4],[4,5]] 输出:2
解释:气球可以用2支箭来爆破: - 在x = 2处发射箭,击破气球[1,2]和[2,3]。 - 在x = 4处射出箭,击破气球[3,4]和[4,5]。
提示:
局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。
为了让气球尽可能的重叠,需要对数组进行排序。如果气球重叠了,重叠气球中右边边界的最小值之前的区间一定需要一个弓箭。
#include
using namespace std;
static bool cmp(vector& a,vector& b){
return a[0] < b[0];
}
int findMinArrowShots(vector>& points) {
if(points.size() == 0) return 0;
sort(points.begin(), points.end(), cmp);
int result = 1;
for (int i = 0; i < points.size() - 1; i++)
{
if(points[i][1] < points[i + 1][0]){
result++;
}
else{
points[i + 1][1] = min(points[i][1], points[i + 1][1]);
}
}
return result;
}
int main(void){
int a;//行数
int b;//列数
cin >> a >> b;
vector> points(a,vector(b));
for (int i = 0; i < a; ++i) {
for (int j = 0; j < b; ++j) {
cin >> points[i][j];
}
}
int result;
result = findMinArrowShots(points);
cout << result << endl;
return 0;
}
/*
示例1:
4
2
10 16
2 8
1 6
7 12
示例2:
4
2
1 2
3 4
5 6
7 8
示例3:
4
2
1 2
2 3
3 4
4 5
* */
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]] 输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ] 输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: intervals = [ [1,2], [2,3] ] 输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
提示:
这道题的思路和用最少的箭射气球非常相似,也是需要进行数组的排序,根据start来进行升序排序,这样易于判断重叠。
它们之间的区别,在于这道题除了判断出重叠外还要进行移除,局部最优---两两比较,发现重叠移除end大的元素,记录去除次数。整体最优---移除区间的最小数量。
#include
using namespace std;
static const bool cmp(vector& a, vector& b){
return a[0] < b[0];
}
int eraseOverlapIntervals(vector>& intervals) {
if(intervals.size() == 1) return 0;
sort(intervals.begin(), intervals.end(), cmp);
int count = 0; //记录移除次数
for (int i = 1; i < intervals.size(); i++)
{
//只需判断右边界大小,来选则留下哪个
if(intervals[i - 1][1] > intervals[i][0]){
count++;
if(intervals[i - 1][1] < intervals[i][1]){
intervals[i] = intervals[i - 1]; //移除方式---覆盖
}
}
}
return count;
}
int main(void){
int a;//行数
int b;//列数
cin >> a >> b;
vector> intervals(a,vector(b));
for (int i = 0; i < a; ++i) {
for (int j = 0; j < b; ++j) {
cin >> intervals[i][j];
}
}
int result;
result = eraseOverlapIntervals(intervals);
cout << result << endl;
return 0;
}
/*
示例1:
4
2
1 2
2 3
3 4
1 3
示例2:
3
2
1 2
1 2
1 2
示例3:
2
2
1 2
2 3
* */
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
示例 1:输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8]
解释: 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec" 输出:[10]
提示:
这道题的重点在于找到每个字母的最远边界---即出现的最右位置。
如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
#include
using namespace std;
vector partitionLabels(string s) {
int hash[27] = {0};
for (int i = 0; i < s.size(); i++) { // 统计每一个字符最后出现的位置
hash[s[i] - 'a'] = i;
}
vector result;
int left = 0;
int right = 0;
for (int i = 0; i < s.size(); i++)
{
right = max(right, hash[s[i] - 'a']);
if(right == i){
result.push_back(right - left + 1);
left = right + 1;
}
}
return result;
}
int main(void){
string s;
getline(cin,s);
vector result;
result = partitionLabels(s);
int size = result.size();
for (int i = 0; i < size - 1; ++i) {
cout << result[i] << ",";
}
cout << result[size - 1] << endl;
return 0;
}
/*
示例1:
ababcbacadefegdehijhklij
示例2:
eccbbbbdec
* */
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]] 输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
提示:
这道题目和射爆气球、无重叠区间类似,需要注意的地方有:
#include
using namespace std;
static bool cmp (const vector& a, const vector& b) {
return a[0] < b[0];
}
vector> merge(vector>& intervals) {
vector> result;
if(intervals.size() == 0) return result;
sort(intervals.begin(), intervals.end(), cmp);
bool flag = false; // 标记最后一个区间有没有合并
int length = intervals.size();
//合并结束后,将结果放入result
for (int i = 1; i < length; i++)
{
int start = intervals[i - 1][0];
int end = intervals[i - 1][1];
while (i < length && intervals[i][0] <= end) { // 合并区间
end = max(end, intervals[i][1]); // 不断更新右区间
if (i == length - 1) flag = true; // 最后一个区间也合并了
i++; // 继续合并下一个区间
}
result.push_back({start, end});
}
// 如果最后一个区间没有合并,将其加入result
if (flag == false) {
result.push_back({intervals[length - 1][0], intervals[length - 1][1]});
}
return result;
}
int main(void){
int a;
int b;
cin >> a >> b;
vector> intervals(a,vector(b));
for (int i = 0; i < a; ++i) {
for (int j = 0; j < b; ++j) {
cin >> intervals[i][j];
}
}
vector> result;
result = merge(intervals);
for (int i = 0; i < result.size(); ++i) {
for (int j = 0; j < 2; ++j) {
cout << result[i][j] << " ";
}
cout << endl;
}
return 0;
}
/*
示例1:
4
2
1 3
2 6
8 10
15 18
示例2:
2
2
1 4
4 5
* */
当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
示例 1:
输入: n = 10 输出: 9
示例 2:
输入: n = 1234 输出: 1234
示例 3:
输入: n = 332 输出: 299
提示:
需要注意的是,①部分递增的情况,②记录从哪开始赋9。
局部最优:遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]--,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。
全局最优:得到小于等于N的最大单调递增的整数。
#include
using namespace std;
int monotoneIncreasingDigits(int n) {
//转换成string字符串操作方便
string strNum = to_string(n);
int flag = strNum.size(); //用来标记从哪里开始赋值9
for (int i = strNum.size() - 1; i > 0; i--) {
if (strNum[i - 1] > strNum[i] ) {
flag = i;
strNum[i - 1]--;
}
}
for (int i = flag; i < strNum.size(); i++) {
strNum[i] = '9';
}
return stoi(strNum);
}
int main(void){
int n;
cin >> n;
int result;
result = monotoneIncreasingDigits(n);
cout << result << endl;
return 0;
}
/*
示例1:
10
示例2:
1234
示例3:
332
* */