Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 80cb4d9

Browse files
Update
1 parent f881e3e commit 80cb4d9

File tree

5 files changed

+149
-17
lines changed

5 files changed

+149
-17
lines changed

pics/763.划分字母区间.png

15.5 KB
Loading

problems/0435.无重叠区间.md

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。
5050

51-
如果按照左边界排序,还从左向右遍历的话,要处理各个区间右边界的各种情况,就很复杂了
51+
如果按照左边界排序,还从左向右遍历的话,其实也可以,逻辑会有所不同
5252

5353
一些同学做这道题目可能真的去模拟去重复区间的行为,这是比较麻烦的,还要去删除区间。
5454

@@ -126,7 +126,67 @@ public:
126126

127127
**所以我把本题的难点也一一列出,帮大家不仅代码看的懂,想法也理解的透彻!**
128128

129+
# 补充
130+
131+
本题其实和[贪心算法:用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
132+
133+
[贪心算法:用最少数量的箭引爆气球](https://mp.weixin.qq.com/s/HxVAJ6INMfNKiGwI88-RFw)代码稍做修改,别可以AC本题。
134+
135+
```C++
136+
class Solution {
137+
public:
138+
// 按照区间右边界排序
139+
static bool cmp (const vector<int>& a, const vector<int>& b) {
140+
return a[1] < b[1];
141+
}
142+
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
143+
if (intervals.size() == 0) return 0;
144+
sort(intervals.begin(), intervals.end(), cmp);
145+
146+
int result = 1; // points 不为空至少需要一支箭
147+
for (int i = 1; i < intervals.size(); i++) {
148+
if (intervals[i][0] >= intervals[i - 1][1]) {
149+
result++; // 需要一支箭
150+
}
151+
else { // 气球i和气球i-1挨着
152+
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]); // 更新重叠气球最小右边界
153+
}
154+
}
155+
return intervals.size() - result;
156+
}
157+
};
158+
```
159+
160+
这里按照 作弊案件遍历,或者按照右边界遍历,都可以AC,具体原因我还没有仔细看,后面有空再补充。
161+
```C++
162+
class Solution {
163+
public:
164+
// 按照区间左边界排序
165+
static bool cmp (const vector<int>& a, const vector<int>& b) {
166+
return a[0] < b[0];
167+
}
168+
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
169+
if (intervals.size() == 0) return 0;
170+
sort(intervals.begin(), intervals.end(), cmp);
171+
172+
int result = 1; // points 不为空至少需要一支箭
173+
for (int i = 1; i < intervals.size(); i++) {
174+
if (intervals[i][0] >= intervals[i - 1][1]) {
175+
result++; // 需要一支箭
176+
}
177+
else { // 气球i和气球i-1挨着
178+
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]); // 更新重叠气球最小右边界
179+
}
180+
}
181+
return intervals.size() - result;
182+
}
183+
};
184+
185+
```
186+
129187
循序渐进学算法,认准「代码随想录」就够了,值得介绍给身边的朋友同学们!
130188

131189
> 我是[程序员Carl](https://github.com/youngyangyang04),组队刷题可以找我,本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已收录,更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在:[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png),期待你的关注!
132190
191+
192+
是不是尽可能重叠,其实它都在那里,没有尽可能这一说

problems/0494.目标和.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,33 +70,68 @@ public:
7070

7171
## 动态规划
7272

73-
使用背包要明确dp[i]表示的是什么,i表示的又是什么?
73+
如何转化为01背包问题呢。
7474

75-
填满i(包括i)这么大容积的包,有dp[i]种方法
75+
假设加法的总和为x,那么减法对应的总和就是sum - x
7676

77+
所以我们要求的是 x - (sum - x) = S
78+
79+
x = (S + sum) / 2
80+
81+
此时问题就转化为,装满容量为x背包,有几种方法。
82+
83+
大家看到(S + sum) / 2 应该担心计算的过程中向下取整有没有影响。
84+
85+
这么担心就对了,例如sum 是5,S是2的话其实就是无解的,所以:
86+
87+
```
88+
if ((S + sum) % 2 == 1) return 0; // 此时没有方案,两个int相加的时候要各位小心数值溢出的问题
89+
```
90+
91+
看到这种表达式,应该本能的反应,两个int相加数值可能溢出的问题,当然本题并没有溢出。
92+
93+
在回归到01背包问题,
94+
95+
这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。
96+
97+
本题是装满有几种方法。
98+
99+
* 确定dp数组以及下标的含义
100+
101+
dp[j] 表示:填满j(包括j)这么大容积的包,有dp[i]种方法
102+
103+
* 确定递推公式
104+
105+
有哪些来源可以推出dp[j]呢,只有dp[j - nums[i]]
106+
107+
那么dp[j] 应该是 dp[j] + dp[j - nums[i]]**这块需要好好讲讲**
108+
109+
* dp数组如何初始化
110+
* 确定遍历顺序
77111

78112
```
79-
// 时间复杂度O(n^2)
80-
// 空间复杂度可以说是O(n),也可以说是O(1),因为每次申请的辅助数组的大小是一个常数
81113
class Solution {
82114
public:
83115
int findTargetSumWays(vector<int>& nums, int S) {
84116
int sum = 0;
85117
for (int i = 0; i < nums.size(); i++) sum += nums[i];
86118
if (S > sum) return 0; // 此时没有方案
87-
if ((S + sum) % 2) return 0; // 此时没有方案,两个int相加的时候要各位小心数值溢出的问题
119+
if ((S + sum) % 2 == 1) return 0; // 此时没有方案,两个int相加的时候要各位小心数值溢出的问题
88120
89121
int bagSize = (S + sum) / 2;
90122
int dp[1001] = {1};
91123
for (int i = 0; i < nums.size(); i++) {
92124
for (int j = bagSize; j >= nums[i]; j--) {
93-
if (j - nums[i] >= 0) dp[j] += dp[j - nums[i]];
125+
dp[j] += dp[j - nums[i]];
94126
}
95127
}
96128
return dp[bagSize];
97129
}
98130
};
99131
```
132+
* 时间复杂度O(n * m),n为正数个数,m为背包容量
133+
* 空间复杂度:O(n),也可以说是O(1),因为每次申请的辅助数组的大小是一个常数
134+
100135
dp数组中的数值变化:(从[0 - 4]
101136

102137
```

problems/0763.划分字母区间.md

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1-
## 题目链接
2-
https://leetcode-cn.com/problems/partition-labels/
1+
> 看起来有点难,看仅仅是看起来难而已
32
4-
## 思路
3+
# 763.划分字母区间
54

6-
一想到分割字符串就想到了回溯,但本题其实不用那么复杂。
5+
题目链接: https://leetcode-cn.com/problems/partition-labels/
6+
7+
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
8+
9+
示例:
10+
输入:S = "ababcbacadefegdehijhklij"
11+
输出:[9,7,8]
12+
解释:
13+
划分结果为 "ababcbaca", "defegde", "hijhklij"。
14+
每个字母最多出现在一个片段中。
15+
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
16+
 
17+
提示:
18+
19+
* S的长度在[1, 500]之间。
20+
* S只包含小写字母 'a' 到 'z' 。
21+
22+
# 思路
23+
24+
一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。
25+
26+
题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?
27+
28+
如果没有接触过这种题目的话,还挺有难度的。
29+
30+
在遍历的过程中相当于是要找每一个字母的边界,**如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了**。此时前面出现过所有字母,最远也就到这个边界了。
731

832
可以分为如下两步:
933

1034
* 统计每一个字符最后出现的位置
11-
* 从头遍历字符,如果找到之前字符最大出现位置下标和当前下标相等,则找到了分割点
35+
* 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
1236

1337
如图:
1438

15-
<img src='../pics/763.划分字母区间.png' width=600> </img></div>
39+
![763.划分字母区间](https://img-blog.csdnimg.cn/20201222191924417.png)
1640

1741
明白原理之后,代码并不复杂,如下:
1842

19-
```
43+
```C++
2044
class Solution {
2145
public:
2246
vector<int> partitionLabels(string S) {
@@ -38,4 +62,16 @@ public:
3862
}
3963
};
4064
```
41-
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
65+
66+
* 时间复杂度:O(n)
67+
* 空间复杂度:O(1) 使用的hash数组是固定大小
68+
69+
# 总结
70+
71+
这道题目leetcode标记为贪心算法,说实话,我没有感受到贪心,找不出局部最优推出全局最优的过程。就是用最远出现距离模拟了圈字符的行为。
72+
73+
但这道题目的思路是很巧妙的,所以有必要介绍给大家做一做,感受一下。
74+
75+
就酱,循序渐进寻算法,认准「代码随想录」,直接介绍给身边的朋友同学们!
76+
77+

problems/1049.最后一块石头的重量II.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ dp[j]有两个来源方向,一个是dp[j]自己,一个是dp[j - stones[i]]
3737

3838
for循环遍历石头的数量嵌套一个for循环遍历背包容量,且因为是01背包,每一个物品只使用一次,所以遍历背包容量的时候要倒序。
3939

40-
具体原因我在
40+
具体原因我在[01背包一维数组实现](https://github.com/youngyangyang04/leetcode-master/blob/master/problems/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-2.md)详细讲解过了。大家感兴趣可以去看一下。
4141

4242

43-
```
43+
最后C++代码如下:
44+
```C++
4445
class Solution {
4546
public:
4647
int lastStoneWeightII(vector<int>& stones) {

0 commit comments

Comments
 (0)