Skip to content

Commit c3f6d32

Browse files
committed
fix some bugs
1 parent 0eb9a70 commit c3f6d32

File tree

14 files changed

+102
-32
lines changed

14 files changed

+102
-32
lines changed

Diff for: 1_Divide_and_Conquer/readme.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ $$
169169
在平均情况下,我们选择的一般不是最好也不是最差。和快速排序的平均复杂度证明类似,本算法也可以证明平均复杂度为O(n)
170170

171171
## Problem 3.
172-
> Consider an n-node complete binary tree T, where n = 2^d^ − 1 for some d. Each node v of T is labeled with a real number xv. You may assume that the real numbers labeling the nodes are all distinct. A node v of T is a local minimum if the label x~v~ is less than the label x~w~ for all nodes w that are joined to v by an edge.
172+
> Consider an n-node complete binary tree T, where n = 2^d^ − 1 for some d. Each node v of T is labeled with a real number x~v~. You may assume that the real numbers labeling the nodes are all distinct. A node v of T is a local minimum if the label x~v~ is less than the label x~w~ for all nodes w that are joined to v by an edge.
173173
> You are given such a complete binary tree T, but the labeling is only specified in the following implicit way: for each node v, you can determine the value x~v~ by probing the node v. Show how to find a local minimum of T using only O(logn) probes to the nodes of T.
174174
175175
### 思路与代码
@@ -368,7 +368,7 @@ $$
368368
$$
369369
min(rocks[i] - rocks[i-1] , i ∈ [1,N + 1]) < = d <= L
370370
$$
371-
因此,我们可以用二分的方法猜测该最小距离。设当前的范围[left,right)。 每次我们查看 left 和 right的中点mid,该mid就是我们猜测的最小的距离的最大值,然后对rocks中,间距不大于mid的进行计数(这些就是要删除的点),记为cnt。
371+
因此,我们可以用二分的方法猜测该最小距离。设当前的范围[left,right)。 每次我们查看 left 和 right的中点mid,该mid就是我们猜测的最小的距离的最大值,然后对rocks中,间距**不大于**mid的进行计数(这些就是要删除的点),记为cnt。
372372

373373
- cnt > M: 说明该mid值太大了,需要减小,区间变为 [left,mid)
374374
- cnt < M: 说明mid太小,区间变为 [mid+1,right)
@@ -399,7 +399,7 @@ def solve_largest_minimum_spacing(L, M, N, rocks):
399399
rocks = [0] + rocks + [L]
400400
N += 2
401401
rocks.sort()
402-
left = min(rocks[i] - rocks[i - 1] for i in range(1, N))
402+
left = min(rocks[i] - rocks[i - 1] for i in range(1, N)) # left start with 0 is ok.
403403
return binary_search(left, L + 1, rocks, M, N)
404404

405405
# solve_largest_minimum_spacing(L, M, N, rocks)
@@ -430,7 +430,7 @@ def solve_largest_minimum_spacing(L, M, N, rocks):
430430
## Problem 6.
431431

432432
> Recall the problem of finding the number of inversions. As in the course, we are given a sequence of n numbers a1,··· ,an, which we assume are all distinct, and we difine an inversion to be a pair i < j such that ai > aj.
433-
> We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i < j and ai > 3aj. Given an O(nlogn) algorithm to count the number of significant inversions between two orderings.
433+
> We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i < j and ai > 3a~j~. Given an O(nlogn) algorithm to count the number of significant inversions between two orderings.
434434
435435
### 思路与代码
436436

Diff for: 2_DP/5.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
import collections
55

66

7-
def can_cross(stones):
7+
def can_cross2(stones):
88
n = len(stones)
99
val2id = {stone: i for i, stone in enumerate(stones)}
10-
dp = collections.defaultdict(lambda :collections.defaultdict(int))
10+
dp = collections.defaultdict(lambda: collections.defaultdict(int))
1111
dp[1][0] = True
1212
for j in range(1, n):
13-
for i in dp[j]: # the same as dp[j].keys()
13+
for i in dp[j]: # the same as dp[j].keys()
1414
step = stones[j] - stones[i]
1515
for k in [step + 1, step, step - 1]:
1616
_next = stones[j] + k
@@ -23,6 +23,17 @@ def can_cross(stones):
2323
return False
2424

2525

26+
def can_cross(stones):
27+
dp = {stone: {} for stone in stones}
28+
dp[0][0] = 0
29+
for stone in stones:
30+
for step in dp[stone].values():
31+
for k in [step + 1, step, step - 1]:
32+
if k > 0 and stone + k in dp:
33+
dp[stone + k][stone] = k
34+
return len(dp[stones[-1]].keys()) > 0
35+
36+
2637
if __name__ == '__main__':
2738
print(can_cross([0, 2]))
2839
print(can_cross([0, 1, 3, 5, 6, 8, 12, 17]))

Diff for: 2_DP/readme.md

+23-7
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def rob_circle2(nums):
136136

137137
该题正确性的证明在于对状态转移方程的正确性证明。
138138

139-
对于i不抢,显然只能为dp[i-2],而抢显然为dp[i-2] + nums[i],因此我们只需要取其最大值即可。
139+
对于i不抢,显然只能为dp[i-1],而抢显然为dp[i-2] + nums[i],因此我们只需要取其最大值即可。
140140

141141
环形情况同理。
142142

@@ -151,10 +151,8 @@ def rob_circle2(nums):
151151

152152
## 3. Partition
153153

154-
> Given a string s, partition s such that every substring of the partition is a palindrome. Return
155-
> the minimum cuts needed for a palindrome partitioning of s.
156-
> For example, given s = “aab", return 1 since the palindrome partitioning ["aa", "b"] could
157-
> be produced using 1 cut.
154+
> Given a string s, partition s such that every substring of the partition is a palindrome. Return the minimum cuts needed for a palindrome partitioning of s.
155+
> For example, given s = “aab", return 1 since the palindrome partitioning ["aa", "b"] could be produced using 1 cut.
158156
159157
### 问题分析(最优子结构及DP表达式)
160158

@@ -282,7 +280,7 @@ def decoding_ways(s):
282280

283281
> A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.
284282
>
285-
> If the frog’s last jump was k units, then its next jump must be either k −1,k, or k +1 units. Note that the frog can only jump in the forward direction.
283+
> If the frog’s last jump was k units, then its next jump must be either k −1, k, or k +1 units. Note that the frog can only jump in the forward direction.
286284
>
287285
> Given a list of stones’ positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.
288286
@@ -292,9 +290,27 @@ def decoding_ways(s):
292290

293291
因此对于到达了某一个点,我们可以查看其上一次是从哪个点跳过来的。
294292

293+
设dp[ j ]\[ i ] 为从i到达j 的步数,初始时把所有的石头存放进hash表。然后设置dp\[0][0] = 0. 接着对于每个石头,从可以到达该石头的所有石头中取出步数k(k > 0),然后当前的stone + k看其是否是合法的石头,是的话就有d\[stone + k ][stone] = k
294+
295+
```
296+
def can_cross(stones):
297+
dp = {stone: {} for stone in stones}
298+
dp[0][0] = 0
299+
for stone in stones:
300+
for step in dp[stone].values():
301+
for k in [step + 1, step, step - 1]:
302+
if k > 0 and stone + k in dp:
303+
dp[stone + k][stone] = k
304+
return len(dp[stones[-1]].keys()) > 0
305+
```
306+
307+
308+
309+
### 原来的方法
310+
295311
设dp[ j ]\[ i ] 从i可以到达j,因此,对于点 j,我们只需要查看可以从哪个地方跳转过来(这里假设为i),然后查看其跳跃的距离$step = stones[j] - stones[i]$ , 则下一次的跳的距离为$step + 1, step, step - 1$ ,然后查看下一个点\_id存不存在(用Hash),存在将dp\[\_id][j] 设置为可达 ,若$\_id==n-1$,说明到达了对岸。这样复杂度为O(n^2^)
296312

297-
### 代码
313+
#### 代码
298314

299315
在具体的实现上,使用了类似邻接表的方式来加快速度。
300316

Diff for: 2_DP/readme.pdf

151 KB
Binary file not shown.

Diff for: 3_Greedy/1.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def can_be_a_graph(degrees):
77
d_sum, n = sum(degrees), len(degrees)
88
if d_sum & 1 or n * (n - 1) < d_sum or max(degrees) > n - 1: return False
99
for n in range(n, -1, -1):
10-
degrees.sort(reverse=True) # 可以每一次算完类似合并排序合并过程使得总复杂度为O(n^2)
10+
degrees.sort(reverse=True) # 可以每一次算完类似合并排序合并过程使得总复杂度为O(n^2)
1111
for i in range(1, n):
1212
if degrees[0] <= 0: break
1313
degrees[i] -= 1
@@ -17,6 +17,44 @@ def can_be_a_graph(degrees):
1717
return True
1818

1919

20+
def merge(a, ls, le, re):
21+
t = []
22+
_ls = ls
23+
rs = le
24+
while ls < le and rs < re:
25+
if a[ls] >= a[rs]:
26+
t.append(a[ls])
27+
ls += 1
28+
else:
29+
t.append(a[rs])
30+
rs += 1
31+
for i in range(ls, le):
32+
t.append(a[i])
33+
for i in range(rs, re):
34+
t.append(a[i])
35+
36+
for i in range(_ls, re):
37+
a[i] = t[i - _ls]
38+
39+
40+
def can_be_a_graph2(degrees):
41+
d_sum, n = sum(degrees), len(degrees)
42+
if d_sum & 1 or n * (n - 1) < d_sum or max(degrees) > n - 1: return False
43+
degrees.sort(reverse=True)
44+
while degrees:
45+
k = degrees[0]
46+
for i in range(1, n):
47+
if degrees[0] <= 0: break
48+
degrees[i] -= 1
49+
if degrees[i] < 0: return False
50+
degrees[0] -= 1
51+
if degrees[0] != 0: return False
52+
n -= 1
53+
degrees.pop(0)
54+
merge(degrees, 0, k, n)
55+
return True
56+
57+
2058
if __name__ == '__main__':
2159
test_case = [
2260
[1, 1, 2, 2, 4], # True
@@ -29,4 +67,4 @@ def can_be_a_graph(degrees):
2967
]
3068

3169
for t in test_case:
32-
print(can_be_a_graph(t[:]))
70+
print(can_be_a_graph(t[:]) , can_be_a_graph2(t[:]))

Diff for: 3_Greedy/readme.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
首先,判断满足如下条件:
2525

2626
- 度数和为偶数 (对于无向图,每条边贡献两个度,因此度数和必为偶数)
27-
- 总边数不超过 $ \frac{n*(n-1)}{2} $ 在(完全的简单图,最大度不超过n-1)
27+
- 总边数不超过 $ \frac{n*(n-1)}{2} $ 在(完全的简单图,**最大度不超过n-1**
2828

2929
接着,我们每次排序(逆序),首先把最大的度数(设为k)安排出去,看看是否足够多的点(k个)来满足它需要的边(就是从前往后扫,每个度-1)。如果不够,说明只能安排重边,即不能构成简单图。
3030

@@ -53,8 +53,6 @@ def can_be_a_graph(degrees):
5353

5454
紧接着,我们每次排序(逆序),如果能构成简单的图,度数最大的点x(设度数为k)必然可以从其他较大的k个节点中连接一条边,因此如果有k个节点,那么不会有重边出现,这个点可以安排,如果不满足k个节点,显然不能构成简单图。
5555

56-
57-
5856
### 时间复杂度分析
5957

6058
上述的做法,每次进行排序,每次排序复杂度为O(nlogn),一共有n次排序,因此复杂度O(n^2^logn)
@@ -111,7 +109,7 @@ fte_i_k = fte = max(fte,pte+f[k])
111109

112110
由于f[i] > f[k],那么有fte_k\_i > fte_k\_i\_1(因为f[i] > f[k]并且pte变大了)
113111

114-
而 fte_i_k >= fte\_i\_k\_1(pte变大,但是fk] < f[i]),也就是说,交换i和k不会增大fte的值。
112+
而 fte_i_k >= fte\_i\_k\_1(pte变大,但是f[k] < f[i]),也就是说,交换i和k不会增大fte的值。
115113

116114
因此,我们的贪心算法是正确的。
117115

@@ -155,7 +153,7 @@ def min_radar(points, d):
155153
px = points[0].x + sqrt(d * d - points[0].y * points[0].y)
156154
ans = 1
157155
for i in range(1,len(points)):
158-
if (px - points[i].x)**2 + points[i].y * points[i].y <= d*d: continue
156+
if (px - points[i].x)**2 + points[i].y ** 2 <= d*d: continue
159157
cx = points[i].x + sqrt(d * d - points[i].y * points[i].y)
160158
if cx < px:
161159
px = cx

Diff for: 4_LP/readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ $$
163163
\end{array}\nonumber
164164
$$
165165

166-
还没写完
166+
167167

168168

169169

Diff for: 5_Network_flow/Assignment5_NF.pdf

48.2 KB
Binary file not shown.

Diff for: 5_Network_flow/readme.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test(m, n, row, col):
131131
遍历E中的边,重复下述的过程:
132132

133133
- 对于当前的边e~i~,将其容量+1
134-
- 继续运行最大流算法,得到新的最小割 C~i~其大小为|C~i~|
134+
- 继续运行最大流算法,得到新的最小割 C~i~其大小(即最大流流量)为|C~i~|
135135
-|C~i~| = |C|, 说明|C~i~| 也是最小割,就是说G的最小割不唯一,return False
136136
- 恢复e~i~ 的值
137137

@@ -293,11 +293,13 @@ $$
293293
$$
294294
We add a 'weight' which is called y~e~ for every edge.
295295

296-
The two constraints says every path has a non-negative weight and sum of every path's weight must be at least 1. Actually, given a cut A-B(and $s \in A, t\in B$) , if edge e connect A and B(in other words, $u \in A, v\in B\quad(u,v) $ is two vertices of edge e,and $ (u,v)$ is a cut edge ),we set y~e~ = 1, otherwise, we set y~e~ = 0.
296+
The two constraints says every path has a non-negative weight and sum of every path's weight must be at least 1. Actually, given a cut A-B(and $s \in A, t\in B$) , if edge e connect A and B(in other words, $u \in A, v\in B\quad(u,v) $ is two vertices of edge e,and $ (u,v)$ is a cut edge ),we set y~e~ = 1, otherwise, we set y~e~ = 0.
297297

298298
So, the objective function is just to minimize the sum of capacity multiply its weight. Obviously, it is designed to solve the minimum cut problem.
299299

300+
目标函数中 $y_e$ 表示一条边选或者不选,为1选,为2不选,因此目标函数是求最小割。
300301

302+
第一个约束条件表示从s到t中的路径,至少有一条边在割上。
301303

302304

303305

Diff for: 6_NP/Assignment6_NP.pdf

126 KB
Binary file not shown.

Diff for: 6_NP/img/4_true.png

51 Bytes
Loading

Diff for: 6_NP/readme.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ mine consistency problem = { \<G,φ> |给定图G以及部分周围地雷个数
6565

6666
> In the Half-3SAT problem, we are given a 3SAT formula  with n variables and m clauses, where m is even. We wish to determine whether there exists an assignment to the variables of  such that exactly half the clauses evaluate to false and exactly half the clauses evaluate to true. Prove that Half-3SA problem is in NP-complete.
6767
68-
首先,给定一个有m个子句的 Half-3SAT问题,显然,我们可以在多项式时间内判断其是否是可满足的,即是否有$\frac{m}{2}$个子句为真,$\frac{m}{2}$个子句为假,因此, Half-3SAT可以在多项式时间验证,它是NP问题。
68+
首先,给定一个有m个子句的 Half-3SAT问题以及n个变量的赋值,显然,我们可以在多项式时间内判断其是否是可满足的,即是否有$\frac{m}{2}$个子句为真,$\frac{m}{2}$个子句为假,因此, Half-3SAT可以在多项式时间验证,它是NP问题。
6969

7070
为了证明Half-3SAT是NP完全问题,我们只需要证明 $3SAT \leq_p Half-3SAT$ 问题即可。
7171

@@ -91,39 +91,44 @@ mine consistency problem = { \<G,φ> |给定图G以及部分周围地雷个数
9191

9292
## 4. Solitaire Game
9393

94-
> In the following solitaire game, you are given an n X n board. On each of its n^2^ positions lies either a blue stone, a red stone, or nothing at all. You play by removing stones from the board so that each column contains only stones of a single color and each row contains at least one stone. You win if you achieve this objective.
94+
> In the following solitaire game, you are given an n x n board. On each of its n^2^ positions lies either a blue stone, a red stone, or nothing at all. You play by removing stones from the board so that each **column** contains **only stones of a single color** and each **row** contains **at least one stone**. You win if you achieve this objective.
9595
>
9696
> Winning may or may not be possible, depending upon the initial configuration. You must determine the given initial con guration is a winnable game configuration. Let **SOLITAIRE** = { \<G> |G is a winnable game configuration}. Prove that SOLITAIRE is NP-complete.
9797
9898
首先,给定最终板子上的红色和蓝色石头的分布情况,我们只需要对每一列扫描,看其每一列是否颜色单一,然后对每一行扫描,看其每一行是否至少有一个石头。这只需要花费O(n^2^)的时间,因此Solitaire Game是NP问题。
9999

100100
为了证明Solitaire Game是NP完全问题,我们只需要证明 $3SAT \leq_p Solitaire\quad Game$ 问题即可。
101101

102-
对于一个3SAT问题,假设有N个变量,M个子句,那么我们设n = max(M,N)
102+
对于一个3SAT问题,假设有N个变量,M个子句,若 $N \ne M$:
103+
104+
- N > M: 那么创建N - M个子句 $p \lor \neg p \lor q$的子句,这些子句不会对3SAT解照成影响。
105+
- N < M: 那么创建M - N个不会被使用的变量。
106+
107+
接着我们设我们设n = max(M,N),创建$n \times n$的板子。
103108

104109
我们将每一行对应一个子句,每一列对应一个变量。对于子句中$x_i$的变量,我们用蓝色圆形石头表示,对于子句中$\neg x_i$的变量,我们用红色三角形的石头表示。
105110

106111
因此,比如$ (x_1 \lor x_2 \lor\neg x_3) \land (x_3 \lor \neg x_4 \lor x_5) $ 如下图:
107112

108113
![4](./img/4.png)
109114

110-
若变量$x_i = True$,我们移除红色三角形石头,否则,移除蓝色圆形石头。
115+
若变量$x_i$为True,我们移除红色三角形石头,否则,移除蓝色圆形石头。
111116

112117
以$x_1 = True ,x_2 = True ,x_3 = False,x_4=True,x_5 =True $ 如下图所示
113118

114119
![4_true](./img/4_true.png)
115120

116121

117122

118-
这个赋值在SAT是为真的,而我们的Solitaire Game同样可以为真,在下面的$t_3 , t_4,t_5$ 中,只需要保证每一行都有一个石头,该石头与该列赋值一致即可(如$x_1 = True$那么放置蓝色石头,$x_4 = True$放置红色石头)。
123+
这个赋值在SAT是为真的,而我们的Solitaire Game同样可以为真,在下面的$t_3 , t_4,t_5$ 中,只需要保证每一行都有一个石头,该石头与该列赋值一致即可(如$x_1 = True$那么放置蓝色石头,$x_3 = False$放置红色石头)。
119124

120125
而3SAT赋值为假的情况又如何呢?$x_1 = False,x_2 = False,x_3 = True,x_4=True,x_5 =False$为使得$ (x_1 \lor x_2 \lor\neg x_3) \land (x_3 \lor \neg x_4 \lor x_5) $ 为假的一组赋值。移除对应的石头有:
121126

122127
![4_false](./img/4_false.png)
123128

124-
而无论下面的$t_3 , t_4,t_5$ 如何赋值,第一行都是空的,不符合条件,Solitaire Game无解。
129+
而无论下面的$t_3 , t_4,t_5$ 如何赋值,第一行都是空的,不符合条件,Solitaire Game无解。
125130

126-
其实上述的移除石头的过程原理是:用列来保证只有一种颜色(要么赋值为真-蓝色,要么赋值为假-红色),而用行来保证每行至少有一个石头(该变量为真石头得到保留)。
131+
其实上述的移除石头的过程原理是:**用列来保证只有一种颜色**(要么赋值为真-蓝色,要么赋值为假-红色),**用行来保证每行至少有一个石头**(该变量为真石头得到保留)。
127132

128133
因此有$3SAT \leq_p Solitaire\quad Game$ ,所以Solitaire Game是NP完全问题。
129134

Diff for: 6_NP/readme.pdf

11.7 KB
Binary file not shown.

Diff for: readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
## 支持
2525

26-
觉得好的话可以点击 **star**支持 或者**打赏**一点馒头钱=v=
26+
觉得好的话可以点击 **star**支持 **打赏**一点馒头钱=v=
2727

2828

2929

0 commit comments

Comments
 (0)