Skip to content

Commit 585a76f

Browse files
authored
Merge branch 'master' into patch-1
2 parents 42202c3 + d5e3d79 commit 585a76f

9 files changed

Lines changed: 131 additions & 22 deletions

File tree

src/data_structures/fenwick.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ int point_query(int idx) {
380380

381381
Note: of course it is also possible to increase a single point $A[i]$ with `range_add(i, i, val)`.
382382

383-
### 3. Range Updates and Range Queries
383+
### 3. Range Update and Range Query
384384

385385
To support both range updates and range queries we will use two BITs namely $B_1[]$ and $B_2[]$, initialized with zeros.
386386

src/dynamic_programming/intro-to-dp.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ $$\text{work per subproblem} * \text{number of subproblems}$$
8181

8282
Using a binary search tree (map in C++) to save states will technically result in $O(n \log n)$ as each lookup and insertion will take $O(\log n)$ work and with $O(n)$ unique subproblems we have $O(n \log n)$ time.
8383

84-
This approach is called top-down, as we can call the function with a query value and the calculation starts going from the top (queried value) down to the bottom (base cases of the recursion), and makes shortcuts via memorization on the way.
84+
This approach is called top-down, as we can call the function with a query value and the calculation starts going from the top (queried value) down to the bottom (base cases of the recursion), and makes shortcuts via memoization on the way.
8585

8686
## Bottom-up Dynamic Programming
8787

@@ -130,24 +130,34 @@ That's it. That's the basics of dynamic programming: Don't repeat work you've do
130130
One of the tricks to getting better at dynamic programming is to study some of the classic examples.
131131

132132
## Classic Dynamic Programming Problems
133-
- 0-1 Knapsack
134-
- Subset Sum
135-
- Longest Increasing Subsequence
136-
- Counting all possible paths from top left to bottom right corner of a matrix
137-
- Longest Common Subsequence
138-
- Longest Path in a Directed Acyclic Graph (DAG)
139-
- Coin Change
140-
- Longest Palindromic Subsequence
141-
- Rod Cutting
142-
- Edit Distance
143-
- Bitmask Dynamic Programming
144-
- Digit Dynamic Programming
145-
- Dynamic Programming on Trees
133+
| Name | Description/Example |
134+
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
135+
| 0-1 knapsack | Given $W$, $N$, and $N$ items with weights $w_i$ and values $v_i$, what is the maximum $\sum_{i=1}^{k} v_i$ for each subset of items of size $k$ ($1 \le k \le N$) while ensuring $\sum_{i=1}^{k} w_i \le W$? |
136+
| Subset Sum | Given $N$ integers and $T$, determine whether there exists a subset of the given set whose elements sum up to the $T$. |
137+
| Longest Increasing Subsequence | You are given an array containing $N$ integers. Your task is to determine the LCS in the array, i.e., LCS where every element is larger than the previous one. |
138+
| Counting all possible paths in a matrix. | Given $N$ and $M$, count all possible distinct paths from $(1,1)$ to $(N, M)$, where each step is either from $(i,j)$ to $(i+1,j)$ or $(i,j+1)$. |
139+
| Longest Common Subsequence | You are given strings $s$ and $t$. Find the length of the longest string that is a subsequence of both $s$ and $t$. |
140+
| Longest Path in a Directed Acyclic Graph (DAG) | Finding the longest path in Directed Acyclic Graph (DAG). |
141+
| Longest Palindromic Subsequence | Finding the Longest Palindromic Subsequence (LPS) of a given string. |
142+
| Rod Cutting | Given a rod of length $n$ units, Given an integer array cuts where cuts[i] denotes a position you should perform a cut at. The cost of one cut is the length of the rod to be cut. What is the minimum total cost of the cuts. |
143+
| Edit Distance | The edit distance between two strings is the minimum number of operations required to transform one string into the other. Operations are ["Add", "Remove", "Replace"] |
144+
145+
## Related Topics
146+
* Bitmask Dynamic Programming
147+
* Digit Dynamic Programming
148+
* Dynamic Programming on Trees
146149

147150
Of course, the most important trick is to practice.
148151

149152
## Practice Problems
150153
* [LeetCode - 1137. N-th Tribonacci Number](https://leetcode.com/problems/n-th-tribonacci-number/description/)
151154
* [LeetCode - 118. Pascal's Triangle](https://leetcode.com/problems/pascals-triangle/description/)
152155
* [LeetCode - 1025. Divisor Game](https://leetcode.com/problems/divisor-game/description/)
156+
* [Codeforces - Zuma](https://codeforces.com/problemset/problem/607/b)
157+
* [LeetCode - 221. Maximal Square](https://leetcode.com/problems/maximal-square/description/)
158+
* [LeetCode - 1039. Minimum Score Triangulation of Polygon](https://leetcode.com/problems/minimum-score-triangulation-of-polygon/description/)
159+
160+
## Dp Contests
161+
* [Atcoder - Educational DP Contest](https://atcoder.jp/contests/dp/tasks)
162+
* [CSES - Dynamic Programming](https://cses.fi/problemset/list/)
153163

src/graph/2SAT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,4 @@ void add_disjunction(int a, bool na, int b, bool nb) {
168168
* [UVA: Rectangles](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3081)
169169
* [Codeforces : Radio Stations](https://codeforces.com/problemset/problem/1215/F)
170170
* [CSES : Giant Pizza](https://cses.fi/problemset/task/1684)
171+
* [Codeforces: +-1](https://codeforces.com/contest/1971/problem/H)

src/graph/bridge-searching-online.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ int find_cc(int v) {
174174
}
175175

176176
void make_root(int v) {
177-
v = find_2ecc(v);
178177
int root = v;
179178
int child = -1;
180179
while (v != -1) {

src/graph/bridge-searching.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ The implementation needs to distinguish three cases: when we go down the edge in
4040

4141
To implement this, we need a depth first search function which accepts the parent vertex of the current node.
4242

43-
```cpp
43+
For the cases of multiple edges, we need to be careful when ignoring the edge from the parent. To solve this issue, we can add a flag `parent_skipped` which will ensure we only skip the parent once.
44+
45+
```{.cpp file=bridge_searching_offline}
46+
void IS_BRIDGE(int v,int to); // some function to process the found bridge
4447
int n; // number of nodes
4548
vector<vector<int>> adj; // adjacency list of graph
4649

@@ -51,8 +54,12 @@ int timer;
5154
void dfs(int v, int p = -1) {
5255
visited[v] = true;
5356
tin[v] = low[v] = timer++;
57+
bool parent_skipped = false;
5458
for (int to : adj[v]) {
55-
if (to == p) continue;
59+
if (to == p && !parent_skipped) {
60+
parent_skipped = true;
61+
continue;
62+
}
5663
if (visited[to]) {
5764
low[v] = min(low[v], tin[to]);
5865
} else {

src/graph/depth-first-search.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ For more details check out the implementation.
5757

5858
## Classification of edges of a graph
5959

60-
We can classify the edges using the entry and exit time of the end nodes $u$ and $v$ of the edges $(u,v)$.
60+
We can classify the edges of a graph, $G$, using the entry and exit time of the end nodes $u$ and $v$ of the edges $(u,v)$.
6161
These classifications are often used for problems like [finding bridges](bridge-searching.md) and [finding articulation points](cutpoints.md).
6262

6363
We perform a DFS and classify the encountered edges using the following rules:
@@ -74,7 +74,15 @@ If $v$ is visited before $u$:
7474
* Forward Edges - If $v$ is a descendant of $u$, then edge $(u, v)$ is a forward edge. In other words, if we already visited and exited $v$ and $\text{entry}[u] < \text{entry}[v]$ then the edge $(u,v)$ forms a forward edge.
7575
* Cross Edges: if $v$ is neither an ancestor or descendant of $u$, then edge $(u, v)$ is a cross edge. In other words, if we already visited and exited $v$ and $\text{entry}[u] > \text{entry}[v]$ then $(u,v)$ is a cross edge.
7676

77-
Note: Forward edges and cross edges only exist in directed graphs.
77+
**Theorem**. Let $G$ be an undirected graph. Then, performing a DFS upon $G$ will classify every encountered edge as either a tree edge or back edge, i.e., forward and cross edges only exist in directed graphs.
78+
79+
Suppose $(u,v)$ is an arbitrary edge of $G$ and without loss of generality, $u$ is visited before $v$, i.e., $\text{entry}[u] < \text{entry}[v]$. Because the DFS only processes edges once, there are only two ways in which we can process the edge $(u,v)$ and thus classify it:
80+
81+
* The first time we explore the edge $(u,v)$ is in the direction from $u$ to $v$. Because $\text{entry}[u] < \text{entry}[v]$, the recursive nature of the DFS means that node $v$ will be fully explored and thus exited before we can "move back up the call stack" to exit node $u$. Thus, node $v$ must be unvisited when the DFS first explores the edge $(u,v)$ from $u$ to $v$ because otherwise the search would have explored $(u,v)$ from $v$ to $u$ before exiting node $v$, as nodes $u$ and $v$ are neighbors. Therefore, edge $(u,v)$ is a tree edge.
82+
83+
* The first time we explore the edge $(u,v)$ is in the direction from $v$ to $u$. Because we discovered node $u$ before discovering node $v$, and we only process edges once, the only way that we could explore the edge $(u,v)$ in the direction from $v$ to $u$ is if there's another path from $u$ to $v$ that does not involve the edge $(u,v)$, thus making $u$ an ancestor of $v$. The edge $(u,v)$ thus completes a cycle as it is going from the descendant, $v$, to the ancestor, $u$, which we have not exited yet. Therefore, edge $(u,v)$ is a back edge.
84+
85+
Since there are only two ways to process the edge $(u,v)$, with the two cases and their resulting classifications outlined above, performing a DFS upon $G$ will therefore classify every encountered edge as either a tree edge or back edge, i.e., forward and cross edges only exist in directed graphs. This completes the proof.
7886

7987
## Implementation
8088

src/graph/search-for-connected-components.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ void find_comps() {
102102
```
103103

104104
## Practice Problems
105-
- [SPOJ: CCOMPS](http://www.spoj.com/problems/CCOMPS/)
106105
- [SPOJ: CT23E](http://www.spoj.com/problems/CT23E/)
107106
- [CODECHEF: GERALD07](https://www.codechef.com/MARCH14/problems/GERALD07)
108107
- [CSES : Building Roads](https://cses.fi/problemset/task/1666)

src/navigation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ search:
6161
- Advanced
6262
- [Deleting from a data structure in O(T(n) log n)](data_structures/deleting_in_log_n.md)
6363
- Dynamic Programming
64-
- [Introducton to Dynamic Programming](dynamic_programming/intro-to-dp.md)
64+
- [Introduction to Dynamic Programming](dynamic_programming/intro-to-dp.md)
6565
- [Knapsack DP](dynamic_programming/knapsack.md)
6666
- DP optimizations
6767
- [Divide and Conquer DP](dynamic_programming/divide-and-conquer-dp.md)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include <algorithm>
2+
#include <cassert>
3+
#include <vector>
4+
#include <set>
5+
using namespace std;
6+
7+
#include "bridge_searching_offline.h"
8+
9+
vector<pair<int,int>> bridges;
10+
void IS_BRIDGE(int v,int to){
11+
bridges.push_back({v, to});
12+
}
13+
14+
void normalize(vector<pair<int,int>> &v){
15+
for(auto &p : v){
16+
if(p.first > p.second)swap(p.first, p.second);
17+
}
18+
sort(v.begin(), v.end());
19+
}
20+
21+
void test_suite(
22+
int _n,
23+
vector<vector<int>> _adj,
24+
vector<pair<int,int> > expected_bridges){
25+
bridges.clear();
26+
n = _n;
27+
adj = _adj;
28+
find_bridges();
29+
normalize(expected_bridges);
30+
normalize(bridges);
31+
assert(bridges == expected_bridges);
32+
}
33+
34+
int main() {
35+
vector<vector<int>> adj;
36+
auto add_edge = [&](int a,int b){
37+
adj[a].push_back(b);
38+
adj[b].push_back(a);
39+
};
40+
{
41+
int n = 5;
42+
adj.resize(n);
43+
add_edge(0, 1);
44+
add_edge(0, 1);
45+
46+
add_edge(2, 3);
47+
add_edge(3, 4);
48+
vector<pair<int,int> > expected_bridges;
49+
expected_bridges.push_back({2,3});
50+
expected_bridges.push_back({3,4});
51+
// note that 0-1 is not a bridge due to duplicate edges!
52+
test_suite(n, adj, expected_bridges);
53+
adj.clear();
54+
}
55+
{
56+
int n = 7;
57+
adj.resize(n);
58+
add_edge(0, 1);
59+
add_edge(1, 2);
60+
add_edge(2, 3);
61+
add_edge(3, 1);
62+
add_edge(3, 4);
63+
add_edge(0, 4);
64+
add_edge(4, 5);
65+
add_edge(1, 6);
66+
vector<pair<int,int> > expected_bridges;
67+
expected_bridges.push_back({4, 5});
68+
expected_bridges.push_back({1, 6});
69+
test_suite(n, adj, expected_bridges);
70+
adj.clear();
71+
}
72+
{
73+
int n = 4;
74+
adj.resize(n);
75+
add_edge(0, 1);
76+
add_edge(0, 1);
77+
add_edge(1, 2);
78+
add_edge(2, 3);
79+
add_edge(2, 3);
80+
vector<pair<int,int> > expected_bridges;
81+
expected_bridges.push_back({1, 2});
82+
test_suite(n, adj, expected_bridges);
83+
adj.clear();
84+
}
85+
}

0 commit comments

Comments
 (0)