贪心子专题-五、思维题
参考:分享丨【题单】贪心算法(基本贪心策略/反悔/区间/字典序/数学/思维/构造)- 讨论 - 力扣(LeetCode)
一、 从特殊到一般
Leetcode 781. 森林中的兔子
思考:“从特殊到一般”找规律
特殊:
一般:
(上取整转变为下取整的方法参见“数学专题”)
Code:
class Solution { public: int numRabbits(vector<int>& answers) { unordered_map<int, int> cnt; for (int x: answers) cnt[x] ++; int res = 0; for (auto& [x, c]: cnt) res += (c + x) / (x + 1) * (x + 1); return res; } };
二、从一般到特殊
Leetcode 1007. 行相等的最少多米诺旋转
思考:转化/分类讨论/增加setting
目标是让第一排或者第二排的所有数都相同,这个「所有」必然包含第一个骨牌中的数。所以要么都变成 tops[0],要么都变成 bottoms[0]。计算这两种情况,取最小值。
minRot起到了增加一个条件,更好的解决问题的作用。
Code:
class Solution { public: int minDominoRotations(vector<int>& tops, vector<int>& bottoms) { auto min_rot = [&](int target) -> int { int to_top = 0, to_bottom = 0; for (int i = 0; i < tops.size(); i ++) { int x = tops[i], y = bottoms[i]; if (x != target && y != target) return INT_MAX; if (x != target) to_top ++; // 把y旋转到上半 else if(y != target) to_bottom ++; // x旋转到下半 } return min(to_top, to_bottom); }; int res = min(min_rot(tops[0]), min_rot(bottoms[0])); return res == INT_MAX ? -1: res; } };
感觉这种类似二分的setting,会将一个变量控制不变(增加一个条件),再去解决问题时,会更方便的解决。
Leetcode 2860. 让所有学生保持开心的分组方法数
暴力解法:dfs搜索,超时。。。
贪心:(比大小---->sort)遇事不决先排序
由于排序后,选中的人数就是i + 1,所以判断条件就变成了nums[i] < i + 1 < nums[i + 1]
最关键的点:①前缀优先选择
②选择中的人数转化为i+1,然后需要满足题目的两个条件(第i位同学满足选中的条件,第i+1位同学需要满足不选中的条件,即可推到全体范围都满足条件)。
class Solution {
public:
int countWays(vector<int>& nums) {
sort(nums.begin(), nums.end());
int res = 0;
// 特判都不来
res = (0 < nums[0]);
// 一般情况
for(int i = 0; i < nums.size() - 1; i ++){ // 减去1后,全来的情况直接特判即可。
if((i + 1) > nums[i] && (i + 1) < nums[i + 1])
res ++;
}
// 特判都来(由于数据范围,总人数是必定>nums[i]的,所以res++)
res = res + 1;
return res;
}
};
其他
Leetcode 最长相邻不相等子序列 I
思路:题意:选一个 words 的子序列,要求相邻字符串对应的 groups[i] 不同。
class Solution { public: vector<string> getLongestSubsequence(vector<string>& words, vector<int>& groups) { int n = words.size(); vector<string> res; for (int i = 0; i < n; i ++) if (i == n - 1 || groups[i] != groups[i + 1]) res.push_back(words[i]); return res; } };
Leetcode 75. 颜色分类
思路:技巧:O(1) 插入元素
简单来说:遍历的时候记录下当前位置的值,之后当前位置无脑变2,p1和p0两个指针跟着擦屁股。p1,p0是nums要变为1或者变为0的位置,最终遍历完成后p0为最后一个0的位置,p1是最后一个1的位置。
class Solution { public: void sortColors(vector<int>& nums) { int p0 = 0, p1 = 0; for (int i = 0; i < nums.size(); i ++) { int x = nums[i]; nums[i] = 2; if (x <= 1) nums[p1 ++] = 1; if (x == 0) nums[p0 ++] = 0; } } };