Day37——贪心Ⅴ
1.leetcode_56合并区间
思路:排序,如果重叠,更新right 为max(right, curVal), 不重叠就加入res,需要单独考虑最后一次,因为每次都是在下一次遍历开始时判断是否加入res,因此 当 i==size()时,单独考虑
static bool cmp(const vector<int>& v1, const vector<int>& v2){
return v1 < v2;
}
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
int right = intervals[0][1];
int left = intervals[0][0];
vector<vector<int>> res;
// for(auto i : intervals) {
// cout << i[0] << " " << i[1] << endl;
// }
vector<int> tmp;
tmp.push_back(left);
for(int i = 1; i <= intervals.size(); i++) {
if(i == intervals.size()) {
tmp.push_back(right);
res.push_back(tmp);
break;
}
// cout << i << " " << right << " ";
if(right < intervals[i][0]) {
tmp.push_back(right);
res.push_back(tmp);
tmp.clear();
left = intervals[i][0];
right = intervals[i][1];
tmp.push_back(left);
} else {
right = max(right, intervals[i][1]);
}
cout << endl;;
}
return res;
}
看题解:解法类似,但更简洁;每次都放入一组区间,每次遍历如果相交更新右边界,不相交插入新区间。
为啥没想到第二种,感觉思维受到了限制,跳脱不出来,只想着按部就班,只想着贪心每一步应该都是最优解。
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), [](const vector<int>& v1, const vector<int>& v2){ return v1 < v2;});
vector<vector<int>> res;
res.push_back(intervals[0]);
for(int i = 1; i < intervals.size(); i++) {
if(intervals[i][0] <= res.back()[1]) {
res.back()[1] = max(intervals[i][1], res.back()[1]);
} else {
res.push_back(intervals[i]);
}
}
return res;
}
时间复杂度: O(nlogn)
空间复杂度: O(logn)
2.leetcode_738单调递增的数字
思路:没思路,看题解
小于等于n的 最大数字,首先想到,如果 nums[i] > nums[i+1], 例如76,那么递增的最大数字应该是(7-1)(9),nums[i]-1, nums[i+1] = 9;这时数字最大,如果从前向后遍历数组,nums[i]>nums[i+1]>nums[i+2],这种情况下,如果nums[i+1] 变为nums[i+1] - 1,那么nums[i]和nums[i+1]之间的关系又不满足,所以应该从后向前遍历数组
int monotoneIncreasingDigits(int n) {
string s = to_string(n);
int flag = s.size();
for(int i = s.size() -1; i > 0; i--) {
if(s[i-1] > s[i]) {
flag = i;
s[i-1]--;
}
}
for(int i = flag; i < s.size(); i++)
s[i] = '9';
return stoi(s);
}
3.leetcode_968监控二叉树![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/879c1bf6a2fb4146b0bb04f718fd4a62.png)
思路:放哪里最合适,首先不能放在头节点和叶子节点,因为摄像机涵盖了上中下三层,放中间才能最大程度覆盖,那么就有两种放置方式,一种是自顶向下放置,这样的方式模拟一次会发现越往下可能产生的单独节点就越多,因为只要有一个节点未覆盖,就可能产生2^n的节点,因此选择自底向上放置,从叶子节点的上一个节点开始放置摄像机,这就要求我们先遍历叶子节点,选择左右根后序遍历,
怎么选择摄像头呢?遍历到左根节点,然后 递归看根节点的右节点的孩子?
看了下题解,对我很有帮助,用三个状态表示当前节点状态:未覆盖,有摄像头,已覆盖。
如果当前节点的左右节点有任一个处于未覆盖状态,那么curroot就应该放置摄像头,res++,如果子节点右任一个放置了摄像头,那么当前节点就应该是已覆盖,如果两个子节点都是已覆盖状态,那么curroot就是未覆盖状态。自底向上,如果根节点未覆盖 res++,之后返回res
int res;
int backtracking(TreeNode* root) {
if(root == nullptr) {
// cout << "-----4. "<< endl;
return 2;
}
int left = backtracking(root->left);
int right = backtracking(root->right);
// cout << "-----begin: " << left << " " << right << endl;
if(left == 0 || right == 0) {
// cout << "-----1. " << left << " " << right << endl;
res++;
return 1;
} else if(left == 2 && right == 2) {
// cout << "-----2. " << left << " " << right << endl;
return 0;
} else if(left == 1 || right == 1) {
// cout << "-----3. " << left << " " << right << endl;
return 2;
}
return -1;
}
public:
int minCameraCover(TreeNode* root) {
if(backtracking(root) == 0)
res++;
return res;
}