贪心
感觉贪心还是要通过多接触题目积累经验。
文章目录
53. 最大子数组和 + 输出子数组
如果当前和为负数,那只能作负贡献。因此当和为负数就从下一个元素重新开始算,否则加在上一个连续子数组。
int maxSubArray(vector<int>& nums) {
int res = -1e9, n = nums.size();
int st = 0, ed = 0;
int sum = 0;
for (int i = 0; i < n; i ++ )
{
sum += nums[i];
if (sum > res) res = sum, ed = i;
if (sum < 0)
{
sum = 0;
if (i + 1 < n) st = i + 1;
}
}
cout << st << ' ' << ed << endl;
return res;
}
376. 摆动序列
求数组中作为摆动序列(一大一小交替)的最长子序列的长度。
去除连续相同的数,之后统计峰谷的数量,首尾一定是峰或谷。
int wiggleMaxLength(vector<int>& nums) {
nums.erase(unique(nums.begin(), nums.end()), nums.end());
if (nums.size() <= 2) return nums.size();
int res = 2;
for (int i = 1; i + 1 < nums.size(); i ++ )
{
int a = nums[i - 1], b = nums[i], c = nums[i + 1];
if (b > a && b > c || b < a && b < c) res ++;
}
return res;
}
55. 跳跃游戏
给定每个位置能跳的最远距离,问能否从起点跳到终点。
维护当前能跳到的最远的位置。
45. 跳跃游戏Ⅱ
,1345. 跳跃游戏Ⅳ
都推荐用 bfs
来求解。
bool canJump(vector<int>& nums) {
for (int i = 0, j = 0; i < nums.size(); i ++ )
{
if (i > j) return false;
j = max(j, i + nums[i]);
}
return true;
}
134. 加油站
如果可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回
-1
。如果存在解,则保证它是唯一的。
首先如果从加油站 i
最多只能到加油站 j
的话,那么任意 i <= k <= j
作为出发点都无法抵达终点,因为到加油站 k
时至少带着一定的油量 left
此时都无法抵达 j + 1
,那作为出发点更不可能到达。
因此如果有唯一解的情况下,该解一定存在于 j + 1
及其之后的加油站。
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int res = 0, cur = 0, tot = 0;
int n = gas.size();
for (int i = 0; i < n; i ++ )
{
cur += gas[i] - cost[i];
tot += gas[i] - cost[i];
if (cur < 0)
{
res = i + 1;
cur = 0;
}
}
if (tot < 0) return -1;
return res;
}
135. 分发糖果
需要按照以下要求,给孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。- 相邻两个孩子评分更高的孩子会获得更多的糖果。
请给每个孩子分发糖果,计算并返回需要准备的最少糖果数目。
需要基于两边来定的,可以先考虑一边,再去考虑另一边,也就是两遍贪心的做法。
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> res(n + 1, 1);
for (int i = 1; i < n; i ++ )
if (ratings[i] > ratings[i - 1]) res[i] = res[i - 1] + 1;
for (int i = n - 2; i >= 0; i -- )
if (ratings[i] > ratings[i + 1]) res[i] = max(res[i], res[i + 1] + 1);
int ans = 0;
for (int i = 0; i < n; i ++ ) ans += res[i];
return ans;
}
56. 合并区间
vector<vector<int>> merge(vector<vector<int>>& regs) {
vector<vector<int>> res;
int st = -2e9, ed = -2e9;
sort(regs.begin(), regs.end());
for (auto reg : regs)
{
int l = reg[0], r = reg[1];
if (l <= ed) ed = max(ed, r);
else
{
if (st != -2e9) res.push_back({st, ed});
st = l, ed = r;
}
}
if (st != -2e9) res.push_back({st, ed});
return res;
}
AcWing 905. 区间选点 452. 用最少数量的箭射爆气球
AcWing 908. 最大不相交区间数量
选择尽量少的点,使得每个区间内至少包含一个选出的点。
选择尽可能多的区间,使选择的区间之间两两没有交集。
有多个活动,每个活动有起始时间和结束时间,要求时间不冲突的情况下参加尽可能多的活动。
按区间右端点从小到大排序,左到右遍历,每个未被覆盖的区间选择右端点,因为右端点可以覆盖更多的区间。
bool cmp(PII &a, PII &b)
{
return a.y < b.y;
}
int main()
{
sort(regs.begin(), regs.end(), cmp);
int res = 0, cur = -2e9;
for (int i = 0; i < n; i ++ )
if (cur < regs[i].x)
{
res ++;
cur = regs[i].y;
}
cout << res << endl;
return 0;
}
738. 单调递增的数字
要保证每一位上的数字单调不减,分为以下两种情况:
-
原数字本来就单调不减,那原来的数字就是答案。
-
原来数字出现减小的情况,要找到最大的单调递增的数,那原本就单调不减的部分一定保持不变,如果其中有相同的部分则找到相同部分的起始位置
k
,str[k] --
且之后的数位全置为9
。
int monotoneIncreasingDigits(int n) {
string str = to_string(n);
int k = 0;
while (k < str.size() - 1 && str[k] <= str[k + 1]) k ++;
if (k == str.size() - 1) return n;
while (k && str[k] == str[k - 1]) k --;
str[k] --;
for (int i = k + 1; i < str.size(); i ++ ) str[i] = '9';
return stoi(str);
}
AcWing 906. 区间分组
若干个区间进行分组,要求组数最少且组内区间无交集。
公司安排面试,给出每个应试者各自有空的时间段,它们之间可能有交集,要求对他们进行分组,组内时间不冲突,求最小的组数。
按区间左端点从小到大排序,从左到右遍历区间,判断能否把当前区间放到目前已有的组中(判断区间左端点和每组的右端点大小),如果能则放入,否则新开一个组。
vector<PII> regs;
priority_queue<int, vector<int>, greater<int>> q;
sort(regs.begin(), regs.end());
for (int i = 0; i < n; i ++ )
{
int l = regs[i].x, r = regs[i].y;
if (q.empty() || q.top() >= l) q.push(r);
else q.pop(), q.push(r);
}
cout << q.size() << endl;
AcWing 907. 区间覆盖
给定一个区间
[st, ed]
和若干区间,要求从若干区间中选择最少数量的区间覆盖给定的区间。
按区间左端点从小到大排序,从前往后依次枚举每个区间,在所有能覆盖 st
的区间中,选择右端点最大的区间并更新 st
的值。
可以通过双指针算法来完成上述操作。
sort(segs.begin(), segs.end());
int res = 0;
bool hasFind = false;
for (int i = 0; i < n; i ++ )
{
int j = i, r = -2e9;
while (j < n && segs[j].x <= st)
{
r = max(r, segs[j].y);
j ++;
}
if (r < st) break;
res ++;
if (r >= ed)
{
hasFind = true;
break;
}
st = r;
i = j - 1;
}
if(!hasFind) puts("-1");
else printf("%d\n", res);
AcWing 4966. 填充
有一个长度为
n
的0101
串,其中有一些位置标记为?
,这些位置上可以任意填充0
或者1
,请问如何填充这些位置使得这个0101
串中出现互不重叠的00
和11
子串最多,输出子串个数。
左到右扫描,如果能配对就配对。
AcWing 5308. 公路
公路上有
n
个站点,每个站点都可以加油且每个站点只出售整数升的油。一开始在1
号站点且油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进
d
公里。问开到站点n
最少要多少钱?
用”赊账“的角度考虑一下问题:只用计算汽车跑到第 i
个站点还欠了多少油,为了买最便宜的油,这些欠的油应该在 1 ~ i-1
站点中价格最低的站点买。