From 0fdff0a05d20c4860e292859825274e7a05f7961 Mon Sep 17 00:00:00 2001
From: JJCUBER <34446698+JJCUBER@users.noreply.github.com>
Date: Thu, 18 Apr 2024 21:47:34 -0400
Subject: [PATCH 001/280] Improve title consistency in fenwick.md
---
src/data_structures/fenwick.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/data_structures/fenwick.md b/src/data_structures/fenwick.md
index b6548d291..2ef0db409 100644
--- a/src/data_structures/fenwick.md
+++ b/src/data_structures/fenwick.md
@@ -380,7 +380,7 @@ int point_query(int idx) {
Note: of course it is also possible to increase a single point $A[i]$ with `range_add(i, i, val)`.
-### 3. Range Updates and Range Queries
+### 3. Range Update and Range Query
To support both range updates and range queries we will use two BITs namely $B_1[]$ and $B_2[]$, initialized with zeros.
From 884eb1e0c739fad9d949e4c97f7d1fc86034f4ff Mon Sep 17 00:00:00 2001
From: "Zyad M. Ayad" <36793243+Zyad-Ayad@users.noreply.github.com>
Date: Wed, 24 Apr 2024 15:02:23 +0200
Subject: [PATCH 002/280] Update intro-to-dp.md
---
src/dynamic_programming/intro-to-dp.md | 59 ++++++++++++++++++++------
1 file changed, 46 insertions(+), 13 deletions(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 0102581fa..dbced6187 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -130,19 +130,48 @@ That's it. That's the basics of dynamic programming: Don't repeat work you've do
One of the tricks to getting better at dynamic programming is to study some of the classic examples.
## Classic Dynamic Programming Problems
-- 0-1 Knapsack
-- Subset Sum
-- Longest Increasing Subsequence
-- Counting all possible paths from top left to bottom right corner of a matrix
-- Longest Common Subsequence
-- Longest Path in a Directed Acyclic Graph (DAG)
-- Coin Change
-- Longest Palindromic Subsequence
-- Rod Cutting
-- Edit Distance
-- Bitmask Dynamic Programming
-- Digit Dynamic Programming
-- Dynamic Programming on Trees
+
+ - 0-1 Knapsack
+ - given a set of items, each with a weight and a value, and a knapsack with a maximum capacity.
+
+ - Subset Sum
+ - you are given a set of integers and a target sum. The task is to determine whether there exists a subset of the given set whose elements sum up to the target sum.
+
+ - Longest Increasing Subsequence
+ - It's a problem that asks for the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
+
+ - Counting all possible paths from top left to bottom right corner of a matrix
+ - solved using dynamic programming or recursion with memoization.
+
+ - Longest Common Subsequence
+ - Given two sequences (usually strings), the task is to find the length of the longest subsequence that is common to both sequences.
+
+ - Longest Path in a Directed Acyclic Graph (DAG)
+ - Unlike finding the longest path in a general graph, which is an NP-hard problem, finding the longest path in a DAG can be solved efficiently using dynamic programming.
+
+ - Longest Palindromic Subsequence
+ - Finding the Longest Palindromic Subsequence (LPS) of a given string.
+
+ - Rod Cutting
+ - It involves finding the maximum revenue that can be obtained by cutting a rod of length n into smaller pieces and selling them.
+
+ - Edit Distance
+ - It involves finding the minimum number of operations required to transform one string into another, where the allowed operations are insertion, deletion, or substitution of a single character.
+
+ - Bitmask Dynamic Programming
+ - Bitmask Dynamic Programming is a technique used to solve combinatorial optimization problems, where the state can be represented compactly using bitmasks.
+
+ - Digit Dynamic Programming
+ - Digit Dynamic Programming is a technique used to solve problems related to digits of a number.
+
+ - Dynamic Programming on Trees
+ - a technique used to solve various problems related to trees, such as finding the longest path, computing subtree properties, or counting certain structures within the tree.
+
+ - Range Dynamic Programming
+ - FinRange Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array.
+
+
+
Of course, the most important trick is to practice.
@@ -151,3 +180,7 @@ Of course, the most important trick is to practice.
* [LeetCode - 118. Pascal's Triangle](https://leetcode.com/problems/pascals-triangle/description/)
* [LeetCode - 1025. Divisor Game](https://leetcode.com/problems/divisor-game/description/)
+## Dp Contests
+* [Atcoder - Educational DP Contest](https://atcoder.jp/contests/dp/tasks)
+* [CSES - Dynamic Programming](https://cses.fi/problemset/list/)
+
From 6c693ede87af4e072572c6999c17f7b82ca0703c Mon Sep 17 00:00:00 2001
From: "Zyad M. Ayad" <36793243+Zyad-Ayad@users.noreply.github.com>
Date: Wed, 24 Apr 2024 18:04:49 +0200
Subject: [PATCH 003/280] Update intro-to-dp.md
---
src/dynamic_programming/intro-to-dp.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index dbced6187..2aa2b791a 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -168,7 +168,7 @@ One of the tricks to getting better at dynamic programming is to study some of t
a technique used to solve various problems related to trees, such as finding the longest path, computing subtree properties, or counting certain structures within the tree.
Range Dynamic Programming
- FinRange Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array.
+ Range Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array.
From 762f0766719af2ae3d5179377c31bb4fb791d4b0 Mon Sep 17 00:00:00 2001
From: "Zyad M. Ayad" <36793243+Zyad-Ayad@users.noreply.github.com>
Date: Wed, 24 Apr 2024 18:40:21 +0200
Subject: [PATCH 004/280] Update intro-to-dp.md
Added links to standard problems.
---
src/dynamic_programming/intro-to-dp.md | 58 +++++++-------------------
1 file changed, 16 insertions(+), 42 deletions(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 2aa2b791a..21b629cd5 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -130,48 +130,22 @@ That's it. That's the basics of dynamic programming: Don't repeat work you've do
One of the tricks to getting better at dynamic programming is to study some of the classic examples.
## Classic Dynamic Programming Problems
-
- - 0-1 Knapsack
- - given a set of items, each with a weight and a value, and a knapsack with a maximum capacity.
-
- - Subset Sum
- - you are given a set of integers and a target sum. The task is to determine whether there exists a subset of the given set whose elements sum up to the target sum.
-
- - Longest Increasing Subsequence
- - It's a problem that asks for the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
-
- - Counting all possible paths from top left to bottom right corner of a matrix
- - solved using dynamic programming or recursion with memoization.
-
- - Longest Common Subsequence
- - Given two sequences (usually strings), the task is to find the length of the longest subsequence that is common to both sequences.
-
- - Longest Path in a Directed Acyclic Graph (DAG)
- - Unlike finding the longest path in a general graph, which is an NP-hard problem, finding the longest path in a DAG can be solved efficiently using dynamic programming.
-
- - Longest Palindromic Subsequence
- - Finding the Longest Palindromic Subsequence (LPS) of a given string.
-
- - Rod Cutting
- - It involves finding the maximum revenue that can be obtained by cutting a rod of length n into smaller pieces and selling them.
-
- - Edit Distance
- - It involves finding the minimum number of operations required to transform one string into another, where the allowed operations are insertion, deletion, or substitution of a single character.
-
- - Bitmask Dynamic Programming
- - Bitmask Dynamic Programming is a technique used to solve combinatorial optimization problems, where the state can be represented compactly using bitmasks.
-
- - Digit Dynamic Programming
- - Digit Dynamic Programming is a technique used to solve problems related to digits of a number.
-
- - Dynamic Programming on Trees
- - a technique used to solve various problems related to trees, such as finding the longest path, computing subtree properties, or counting certain structures within the tree.
-
- - Range Dynamic Programming
- - Range Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array.
-
-
-
+| Name | Description | Example Poblems |
+| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
+| 0-1 Knapsack | 0-1 Knapsack | given a set of items, each with a weight and a value, and a knapsack with a maximum capacity. | [D - Knapsack 1 (](https://atcoder.jp/contests/dp/tasks/dp_d)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_d) |
+| 0-1 Knapsack |
+| Subset Sum | you are given a set of integers and a target sum. The task is to determine whether there exists a subset of the given set whose elements sum up to the target sum. | |
+| Longest Increasing Subsequence | It's a problem that asks for the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order. | [Longest Increasing Subsequence - LeetCode](https://leetcode.com/problems/longest-increasing-subsequence/description/) |
+| Counting all possible paths in a matrix. | Solved using dynamic programming or recursion with memoization. | [Unique Paths - LeetCode](https://leetcode.com/problems/unique-paths/description/) |
+| Longest Common Subsequence | Given two sequences (usually strings), the task is to find the length of the longest subsequence that is common to both sequences. | [F - LCS (](https://atcoder.jp/contests/dp/tasks/dp_f)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_f) |
+| Longest Path in a Directed Acyclic Graph (DAG) | Unlike finding the longest path in a general graph, which is an NP-hard problem, finding the longest path in a DAG can be solved efficiently using dynamic programming. | [G - Longest Path (](https://atcoder.jp/contests/dp/tasks/dp_g)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_g) |
+| Longest Palindromic Subsequence | Finding the Longest Palindromic Subsequence (LPS) of a given string. | [Longest Palindromic Subsequence - LeetCode](https://leetcode.com/problems/longest-palindromic-subsequence/description/) |
+| Rod Cutting | It involves finding the maximum revenue that can be obtained by cutting a rod of length n into smaller pieces and selling them. | |
+| Edit Distance | It involves finding the minimum number of operations required to transform one string into another, where the allowed operations are insertion, deletion, or substitution of a single character. | [CSES - Edit Distance](https://cses.fi/problemset/task/1639) |
+| Bitmask Dynamic Programming | Bitmask Dynamic Programming is a technique used to solve combinatorial optimization problems, where the state can be represented compactly using bitmasks. | [Problem - F - Codeforces](https://codeforces.com/contest/1043/problem/F) |
+| Digit Dynamic Programming | Digit Dynamic Programming is a technique used to solve problems related to digits of a number. | [SPOJ.com](http://SPOJ.com) [- Problem PR003004](https://www.spoj.com/problems/PR003004/) |
+| Dynamic Programming on Trees | A technique used to solve various problems related to trees, such as finding the longest path, computing subtree properties, or counting certain structures within the tree. | [P - Independent Set (](https://atcoder.jp/contests/dp/tasks/dp_p)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_p) |
+| Range Dynamic Programming | Range Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array. | |
Of course, the most important trick is to practice.
From fa03985674436f18d4ff96644dcde401820bc0b4 Mon Sep 17 00:00:00 2001
From: "Zyad M. Ayad" <36793243+Zyad-Ayad@users.noreply.github.com>
Date: Wed, 24 Apr 2024 18:41:36 +0200
Subject: [PATCH 005/280] Update intro-to-dp.md
---
src/dynamic_programming/intro-to-dp.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 21b629cd5..eb3302a93 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -133,7 +133,6 @@ One of the tricks to getting better at dynamic programming is to study some of t
| Name | Description | Example Poblems |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| 0-1 Knapsack | 0-1 Knapsack | given a set of items, each with a weight and a value, and a knapsack with a maximum capacity. | [D - Knapsack 1 (](https://atcoder.jp/contests/dp/tasks/dp_d)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_d) |
-| 0-1 Knapsack |
| Subset Sum | you are given a set of integers and a target sum. The task is to determine whether there exists a subset of the given set whose elements sum up to the target sum. | |
| Longest Increasing Subsequence | It's a problem that asks for the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order. | [Longest Increasing Subsequence - LeetCode](https://leetcode.com/problems/longest-increasing-subsequence/description/) |
| Counting all possible paths in a matrix. | Solved using dynamic programming or recursion with memoization. | [Unique Paths - LeetCode](https://leetcode.com/problems/unique-paths/description/) |
From 27cb5c99a6abf35aaa24d25125cdc5100c69784d Mon Sep 17 00:00:00 2001
From: Michael Hayter
Date: Wed, 24 Apr 2024 21:12:07 -0400
Subject: [PATCH 006/280] Update intro-to-dp.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Someone added the r to memoization despite the beginning of the article literally mentioning not to do this 😔.😂
---
src/dynamic_programming/intro-to-dp.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 0102581fa..52064663b 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -81,7 +81,7 @@ $$\text{work per subproblem} * \text{number of subproblems}$$
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.
-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.
+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.
## Bottom-up Dynamic Programming
From 357286307241bcba160af5b2c9ac8a85135a6ca1 Mon Sep 17 00:00:00 2001
From: "Zyad M. Ayad" <36793243+Zyad-Ayad@users.noreply.github.com>
Date: Thu, 25 Apr 2024 10:50:59 +0200
Subject: [PATCH 007/280] Update intro-to-dp.md
- Problems descriptions/examples are short and uniform.
- Added separate list for topic (BitmaskDP, DigitDP and DP on Trees)
---
src/dynamic_programming/intro-to-dp.md | 31 +++++++++++++-------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index eb3302a93..06649670e 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -130,21 +130,22 @@ That's it. That's the basics of dynamic programming: Don't repeat work you've do
One of the tricks to getting better at dynamic programming is to study some of the classic examples.
## Classic Dynamic Programming Problems
-| Name | Description | Example Poblems |
-| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
-| 0-1 Knapsack | 0-1 Knapsack | given a set of items, each with a weight and a value, and a knapsack with a maximum capacity. | [D - Knapsack 1 (](https://atcoder.jp/contests/dp/tasks/dp_d)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_d) |
-| Subset Sum | you are given a set of integers and a target sum. The task is to determine whether there exists a subset of the given set whose elements sum up to the target sum. | |
-| Longest Increasing Subsequence | It's a problem that asks for the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order. | [Longest Increasing Subsequence - LeetCode](https://leetcode.com/problems/longest-increasing-subsequence/description/) |
-| Counting all possible paths in a matrix. | Solved using dynamic programming or recursion with memoization. | [Unique Paths - LeetCode](https://leetcode.com/problems/unique-paths/description/) |
-| Longest Common Subsequence | Given two sequences (usually strings), the task is to find the length of the longest subsequence that is common to both sequences. | [F - LCS (](https://atcoder.jp/contests/dp/tasks/dp_f)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_f) |
-| Longest Path in a Directed Acyclic Graph (DAG) | Unlike finding the longest path in a general graph, which is an NP-hard problem, finding the longest path in a DAG can be solved efficiently using dynamic programming. | [G - Longest Path (](https://atcoder.jp/contests/dp/tasks/dp_g)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_g) |
-| Longest Palindromic Subsequence | Finding the Longest Palindromic Subsequence (LPS) of a given string. | [Longest Palindromic Subsequence - LeetCode](https://leetcode.com/problems/longest-palindromic-subsequence/description/) |
-| Rod Cutting | It involves finding the maximum revenue that can be obtained by cutting a rod of length n into smaller pieces and selling them. | |
-| Edit Distance | It involves finding the minimum number of operations required to transform one string into another, where the allowed operations are insertion, deletion, or substitution of a single character. | [CSES - Edit Distance](https://cses.fi/problemset/task/1639) |
-| Bitmask Dynamic Programming | Bitmask Dynamic Programming is a technique used to solve combinatorial optimization problems, where the state can be represented compactly using bitmasks. | [Problem - F - Codeforces](https://codeforces.com/contest/1043/problem/F) |
-| Digit Dynamic Programming | Digit Dynamic Programming is a technique used to solve problems related to digits of a number. | [SPOJ.com](http://SPOJ.com) [- Problem PR003004](https://www.spoj.com/problems/PR003004/) |
-| Dynamic Programming on Trees | A technique used to solve various problems related to trees, such as finding the longest path, computing subtree properties, or counting certain structures within the tree. | [P - Independent Set (](https://atcoder.jp/contests/dp/tasks/dp_p)[atcoder.jp](http://atcoder.jp)[)](https://atcoder.jp/contests/dp/tasks/dp_p) |
-| Range Dynamic Programming | Range Dynamic Programming is a technique used to solve problems where you need to find optimal solutions for subranges within a given range or array. | |
+| Name | Description/Example |
+| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 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$? |
+| Subset Sum | Given $N$ integers and $T$, determine whether there exists a subset of the given set whose elements sum up to the $T$. |
+| 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. |
+| 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)$. |
+| 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$. |
+| Longest Path in a Directed Acyclic Graph (DAG) | Finding the longest path in Directed Acyclic Graph (DAG). |
+| Longest Palindromic Subsequence | Finding the Longest Palindromic Subsequence (LPS) of a given string. |
+| 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. |
+| 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"] |
+
+## Related Topics
+* Bitmask Dynamic Programming
+* Digit Dynamic Programming
+* Dynamic Programming on Trees
Of course, the most important trick is to practice.
From 115e2b203f6dbdc1f0707b59ccc2a46e24f54fb5 Mon Sep 17 00:00:00 2001
From: NaimSS
Date: Sat, 4 May 2024 11:10:05 +0200
Subject: [PATCH 008/280] fix bug in bridge searching offline algorithm in case
of duplicate edges
---
src/graph/bridge-searching.md | 11 ++++--
test/test_bridge_searching_offline.cpp | 48 ++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 2 deletions(-)
create mode 100644 test/test_bridge_searching_offline.cpp
diff --git a/src/graph/bridge-searching.md b/src/graph/bridge-searching.md
index 2d6596878..50d3de034 100644
--- a/src/graph/bridge-searching.md
+++ b/src/graph/bridge-searching.md
@@ -40,7 +40,10 @@ The implementation needs to distinguish three cases: when we go down the edge in
To implement this, we need a depth first search function which accepts the parent vertex of the current node.
-```cpp
+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.
+
+```{.cpp file=bridge_searching_offline}
+void IS_BRIDGE(int v,int to); // some function to process the found bridge
int n; // number of nodes
vector> adj; // adjacency list of graph
@@ -51,8 +54,12 @@ int timer;
void dfs(int v, int p = -1) {
visited[v] = true;
tin[v] = low[v] = timer++;
+ bool parent_skipped = false;
for (int to : adj[v]) {
- if (to == p) continue;
+ if (to == p && !parent_skipped) {
+ parent_skipped = true;
+ continue;
+ }
if (visited[to]) {
low[v] = min(low[v], tin[to]);
} else {
diff --git a/test/test_bridge_searching_offline.cpp b/test/test_bridge_searching_offline.cpp
new file mode 100644
index 000000000..269254c19
--- /dev/null
+++ b/test/test_bridge_searching_offline.cpp
@@ -0,0 +1,48 @@
+#include
+#include
+#include
+using namespace std;
+
+#include "bridge_searching_offline.h"
+
+vector> bridges;
+void IS_BRIDGE(int v,int to){
+ bridges.push_back({v, to});
+}
+
+void normalize(vector> &v){
+ for(auto &p : v){
+ if(p.first > p.second)swap(p.first, p.second);
+ }
+ sort(v.begin(), v.end());
+}
+
+void test_suite(
+ int _n,
+ vector> _adj,
+ vector > expected_bridges){
+ bridges.clear();
+ n = _n;
+ adj = _adj;
+ find_bridges();
+ normalize(&expected_bridges);
+ normalize(&bridges);
+ assert(bridges == expected_bridges);
+}
+
+int main() {
+ {
+ int n = 5;
+ vector> adj(n);
+ adj[0].push_back(1), adj[1].push_back(0);
+ adj[0].push_back(1), adj[1].push_back(0);
+
+ adj[2].push_back(3), adj[3].push_back(2);
+ adj[3].push_back(4), adj[4].push_back(3);
+ vector > expected_bridges;
+ expected_bridges.push_back({2,3});
+ expected_bridges.push_back({3,4});
+ // note that 0-1 is not a bridge due to duplicate edges!
+ test_suite(n, adj, expected_bridges);
+ }
+}
From cbcce391e0f2b08aed5598b4b93ebfd5b6b94b58 Mon Sep 17 00:00:00 2001
From: NaimSS
Date: Sat, 4 May 2024 11:20:22 +0200
Subject: [PATCH 009/280] improve test cases
---
test/test_bridge_searching_offline.cpp | 50 ++++++++++++++++++++++----
1 file changed, 43 insertions(+), 7 deletions(-)
diff --git a/test/test_bridge_searching_offline.cpp b/test/test_bridge_searching_offline.cpp
index 269254c19..8ca73fba0 100644
--- a/test/test_bridge_searching_offline.cpp
+++ b/test/test_bridge_searching_offline.cpp
@@ -25,24 +25,60 @@ void test_suite(
n = _n;
adj = _adj;
find_bridges();
- normalize(&expected_bridges);
- normalize(&bridges);
+ normalize(expected_bridges);
+ normalize(bridges);
assert(bridges == expected_bridges);
}
int main() {
+ vector> adj;
+ auto add_edge = [&](int a,int b){
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ };
{
int n = 5;
- vector> adj(n);
- adj[0].push_back(1), adj[1].push_back(0);
- adj[0].push_back(1), adj[1].push_back(0);
+ adj.resize(n);
+ add_edge(0, 1);
+ add_edge(0, 1);
- adj[2].push_back(3), adj[3].push_back(2);
- adj[3].push_back(4), adj[4].push_back(3);
+ add_edge(2, 3);
+ add_edge(3, 4);
vector > expected_bridges;
expected_bridges.push_back({2,3});
expected_bridges.push_back({3,4});
// note that 0-1 is not a bridge due to duplicate edges!
test_suite(n, adj, expected_bridges);
+ adj.clear();
+ }
+ {
+ int n = 7;
+ adj.resize(n);
+ add_edge(0, 1);
+ add_edge(1, 2);
+ add_edge(2, 3);
+ add_edge(3, 1);
+ add_edge(3, 4);
+ add_edge(0, 4);
+ add_edge(4, 5);
+ add_edge(1, 6);
+ vector > expected_bridges;
+ expected_bridges.push_back({4, 5});
+ expected_bridges.push_back({1, 6});
+ test_suite(n, adj, expected_bridges);
+ adj.clear();
+ }
+ {
+ int n = 4;
+ adj.resize(n);
+ add_edge(0, 1);
+ add_edge(0, 1);
+ add_edge(1, 2);
+ add_edge(2, 3);
+ add_edge(2, 3);
+ vector > expected_bridges;
+ expected_bridges.push_back({1, 2});
+ test_suite(n, adj, expected_bridges);
+ adj.clear();
}
}
From 2fb1fdb931460dcb7f20cc65d910c54a8d2f4539 Mon Sep 17 00:00:00 2001
From: Daniel Paleka
Date: Fri, 10 May 2024 18:09:59 +0200
Subject: [PATCH 010/280] Fix 2SAT.md
The existing code didn't work out of the box, and `n` was used for two different numbers
---
src/graph/2SAT.md | 110 +++++++++++++++++++++++++---------------------
1 file changed, 59 insertions(+), 51 deletions(-)
diff --git a/src/graph/2SAT.md b/src/graph/2SAT.md
index ac8b83446..e46d245f2 100644
--- a/src/graph/2SAT.md
+++ b/src/graph/2SAT.md
@@ -102,64 +102,72 @@ Below is the implementation of the solution of the 2-SAT problem for the already
In the graph the vertices with indices $2k$ and $2k+1$ are the two vertices corresponding to variable $k$ with $2k+1$ corresponding to the negated variable.
```{.cpp file=2sat}
-int n;
-vector> adj, adj_t;
-vector used;
-vector order, comp;
-vector assignment;
-
-void dfs1(int v) {
- used[v] = true;
- for (int u : adj[v]) {
- if (!used[u])
- dfs1(u);
+struct TwoSatSolver {
+ int n;
+ vector> adj, adj_t;
+ vector used;
+ vector order, comp;
+ vector assignment;
+
+ // n is the number of variables
+ TwoSatSolver(int n) : n(n), adj(2 * n), adj_t(2 * n), used(2 * n), order(), comp(2 * n, -1), assignment(n) {
+ order.reserve(2 * n);
}
- order.push_back(v);
-}
-
-void dfs2(int v, int cl) {
- comp[v] = cl;
- for (int u : adj_t[v]) {
- if (comp[u] == -1)
- dfs2(u, cl);
+
+ void dfs1(int v) {
+ used[v] = true;
+ for (int u : adj[v]) {
+ if (!used[u])
+ dfs1(u);
+ }
+ order.push_back(v);
}
-}
-
-bool solve_2SAT() {
- order.clear();
- used.assign(n, false);
- for (int i = 0; i < n; ++i) {
- if (!used[i])
- dfs1(i);
+
+ void dfs2(int v, int cl) {
+ comp[v] = cl;
+ for (int u : adj_t[v]) {
+ if (comp[u] == -1)
+ dfs2(u, cl);
+ }
}
- comp.assign(n, -1);
- for (int i = 0, j = 0; i < n; ++i) {
- int v = order[n - i - 1];
- if (comp[v] == -1)
- dfs2(v, j++);
+ // m will be the number of vertices in the graph (= 2 * n)
+ bool solve_2SAT(int m) {
+ order.clear();
+ used.assign(m, false);
+ for (int i = 0; i < m; ++i) {
+ if (!used[i])
+ dfs1(i);
+ }
+
+ comp.assign(m, -1);
+ for (int i = 0, j = 0; i < m; ++i) {
+ int v = order[m - i - 1];
+ if (comp[v] == -1)
+ dfs2(v, j++);
+ }
+
+ assignment.assign(m / 2, false);
+ for (int i = 0; i < m; i += 2) {
+ if (comp[i] == comp[i + 1])
+ return false;
+ assignment[i / 2] = comp[i] > comp[i + 1];
+ }
+ return true;
}
- assignment.assign(n / 2, false);
- for (int i = 0; i < n; i += 2) {
- if (comp[i] == comp[i + 1])
- return false;
- assignment[i / 2] = comp[i] > comp[i + 1];
+ void add_disjunction(int a, bool na, int b, bool nb) {
+ // na and nb signify whether a and b are to be negated
+ a = 2 * a ^ na;
+ b = 2 * b ^ nb;
+ int neg_a = a ^ 1;
+ int neg_b = b ^ 1;
+ adj[neg_a].push_back(b);
+ adj[neg_b].push_back(a);
+ adj_t[b].push_back(neg_a);
+ adj_t[a].push_back(neg_b);
}
- return true;
-}
-
-void add_disjunction(int a, bool na, int b, bool nb) {
- // na and nb signify whether a and b are to be negated
- a = 2*a ^ na;
- b = 2*b ^ nb;
- int neg_a = a ^ 1;
- int neg_b = b ^ 1;
- adj[neg_a].push_back(b);
- adj[neg_b].push_back(a);
- adj_t[b].push_back(neg_a);
- adj_t[a].push_back(neg_b);
-}
+};
```
## Practice Problems
From ca045b933ad0aa12ec32b32e50a15729e09ee3d7 Mon Sep 17 00:00:00 2001
From: Sarthak Vishwakarma <97278737+Sarthak3204@users.noreply.github.com>
Date: Sat, 11 May 2024 22:45:33 +0530
Subject: [PATCH 011/280] Update 2SAT.md
Added Codeforces 2SAT question
---
src/graph/2SAT.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/graph/2SAT.md b/src/graph/2SAT.md
index ac8b83446..6ccc938d7 100644
--- a/src/graph/2SAT.md
+++ b/src/graph/2SAT.md
@@ -168,3 +168,4 @@ void add_disjunction(int a, bool na, int b, bool nb) {
* [UVA: Rectangles](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3081)
* [Codeforces : Radio Stations](https://codeforces.com/problemset/problem/1215/F)
* [CSES : Giant Pizza](https://cses.fi/problemset/task/1684)
+ * [Codeforces: +-1](https://codeforces.com/contest/1971/problem/H)
From a3738f580ac978d1a73323761955831197c094cc Mon Sep 17 00:00:00 2001
From: NaimSS
Date: Thu, 16 May 2024 21:20:34 +0200
Subject: [PATCH 012/280] initial version of manhattan mst
---
src/geometry/manhattan-distance.md | 105 +++++++++++++++++++++++++++++
test/manhattan_mst.cpp | 71 +++++++++++++++++++
2 files changed, 176 insertions(+)
create mode 100644 src/geometry/manhattan-distance.md
create mode 100644 test/manhattan_mst.cpp
diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md
new file mode 100644
index 000000000..f0c5eed8f
--- /dev/null
+++ b/src/geometry/manhattan-distance.md
@@ -0,0 +1,105 @@
+---
+tags:
+ - Original
+---
+
+# Manhattan Distance
+
+## Definition
+Consider we have some points on a plane, and define a distance from point $p$ to $q$ as being the sum of the difference between their $x$ and $y$ coordinates:
+
+$d(p,q) = |p.x - q.x| + |p.y - q.y|$
+
+This is informally know as the [Manhattan distance, or taxicab geometry](https://en.wikipedia.org/wiki/Taxicab_geometry), because we can think of the points as being intersections in a well designed city, like manhattan, where you can only move on the streets, as shown in the image below:
+
+This images show some of the smallest paths from one black point to the other, all of them with distance $12$.
+
+There are some interseting tricks and algorithms that can be done with this distance, and we will show some of them here.
+
+## Farthest pair of points in Manhattan Distance
+
+Given $n$ points $P$, we want to find the pair of points $p,q$ that are farther apart, that is, maximize $d(p, q) = |p.x - q.x| + |p.y - q.y|$.
+
+Let's think first in one dimension, so $y=0$. The main observation is that we can bruteforce if $|p.x - q.x|$ is equal to $p.x - q.x$ or $-p.x + q.x$, because if we "miss the sign" of the absolute value, we will get only a smaller value, so it can't affect the answer. More formally, we have that:
+
+$|p.x - q.x| = max(p.x - q.x, -p.x + q.x)$
+
+So for example, we can try to have $p$ such that $p.x$ has the plus sign, and then $q$ must have the negative sign. This way we want to find:
+$max_{p, q \in P}(p.x + (-q.x)) = max_{p \in P}(p.x) + max_{q \in P}( - q.x )$.
+
+Notice that we can extend this idea further for 2 (or more!) dimensions. For $d$ dimensions, we must bruteforce $2^d$ possible values of the signs. For example, if we are in $2$ dimensions and bruteforce that $p$ has both the plus signs we want to find:
+
+$max_{p, q \in P} (p.x + (-q.x)) + (p.y + (-q.y)) = max_{p \in P}(p.x + p.y) + max_{q \in P}(-q.x - q.y)$.
+
+As we made $p$ and $q$ independent, it is now easy to find the $p$ and $q$ that maximize the expression.
+
+## Rotating the points and Chebyshev distance
+
+
+
+## Manhattan Minimum Spanning Tree
+
+The Manhattan MST problem consists of, given some points in the plane, find the edges that connect all the points and have a minimum total sum of weights. The weight of an edge that connects to points is their Manhattan distance. For simplicity, we assume that all points have different locations.
+Here we show a way of finding the MST in $O(n\logn)$ by finding for each point its nearest neighbor in each octant, as represented by the image below. This will give us $O(n)$ candidate edges, which will guarantee that they contain the MST. The final step is then using some standard MST, for example, [Kruskal algorithm using disjoint set union](https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html).
+
+The algorithm show here was first presented in a paper from [H. Zhou, N. Shenoy, and W. Nichollos (2002)](https://ieeexplore.ieee.org/document/913303). There is also another know algorithm that uses a Divide and conquer approach by [J. Stolfi](https://www.academia.edu/15667173/On_computing_all_north_east_nearest_neighbors_in_the_L1_metric), which is also very interesting and only differ in the way they find the nearest neighbor in each octant.
+
+First, let's understand why it is enough to consider only the nearest neighbor in each octant. The idea is to show that for a point s and any two other points $p$ and $q$ in the same octant, $dist(p, q) < max(dist(s, p), dist(s, q))$. This is important, because it shows that if there was a MST where $s$ is connected to both $p$ and $q$, we could erase one of these edges and add the edge $(p,q)$, which would decrease the total cost. To prove, we assume without loss of generality that $p$ and $q$ are in the octanct $R_1$, which is defined by: $x_s \leq x$ and $x_s - y_s > x - y$, and then do some casework. The images below give some intuition on why this is true.
+
+Therefore, the main question is how to find the nearest neighbor in each octant for every single of the $n$ points.
+
+## Nearest Neighbor in each Octant in $O(n\logn)$
+
+For simplicity we focus on the north-east octant. All other directions can be found with the same algorithm by rotating the input.
+
+We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet.
+
+When we add a new point point $p$, for every point $s$ that has it in it's octant we can safely assign $p$ as the nearest neighbor. This is true because their distance is $d(p,s) = |x_p - x_s| + |y_p - y_s| = (x_p + y_p) - (x_s + y_s)$, because $p$ is in the north-east octant. As all the next points will not have a smaller value of $x + y$ because of the process order, $p$ is guaranteed to have the smaller distance. We can then remove all such points from the active set, and finally add $p$ to this set.
+
+The next question is how to efficiently find which points $s$ have $p$ in the north-east octant. That is, which points $s$ satisfy:
+
+- $x_s \leq x_p$
+- $x_p - y_p < x_s - y_s$
+
+Because no points in the active set are in the R_1 of another, we also have that for two points $q_1$ and $q_2$ in the active set, $x_{q_1} \neq x_{q_2}$ and $x_{q_1} < x_{q_2} \implies x_{q_1} - y_{q_1} \leq x_{q_2} - y_{q_2}$.
+
+This means that if we keep the active set ordered by $x$ the candidates $s$ are consecutively placed. We can then find the largest $x_s \leq x_p$ and process the points in decreasing order of $x$ until the second condition $x_p - y_p < x_s - y_s$ breaks (we can actually allow that $x_p - y_p = x_s - y_s$ and that deals with the case of points with equal coordinates). Notice that because we remove from the set right after processing, this will have an amortized complexity of $O(n \log(n))$.
+ Now that we have the nearest point in the north-east direction, we rotate the points and repeat. It is possible to show that actually we also find this way the nearest point in the south-west direction, so we can repeat only 4 times, instead of 8.
+
+In summary we:
+- Sort the points by $x + y$ in non-decreasing order;
+- For every point, we iterate over the active set starting with the point with the largest $x$ such that $x \leq x_p$, and we break the loop if $x_p - y_p \geq x_s - y_s$. For every valid point $s$ we add the edge $(s,p, dist(s,p))$ in our list;
+- We add the point $p$ to the active set;
+- Rotate the points and repeat until we iterate over all the octants.
+- Apply Kruskal algorithm in the list of edges to get the MST.
+
+Below you can find a implementation, based on the one from [KACTL](https://github.com/kth-competitive-programming/kactl/blob/main/content/geometry/ManhattanMST.h).
+
+```{.cpp file=manhattan_mst.cpp}
+vector > manhattan_mst_edges(vector ps){
+ vector ids(ps.size());
+ iota(ids.begin(), ids.end(), 0);
+ vector > edges;
+ for(int rot = 0; rot < 4; rot++){ // for every rotation
+ sort(ids.begin(), ids.end(), [&](int i,int j){
+ return (ps[i].x + ps[i].y) < (ps[j].x + ps[j].y);
+ });
+ map > active; // (xs, id)
+ for(auto i : ids){
+ for(auto it = active.lower_bound(ps[i].x); it != active.end();
+ active.erase(it++)){
+ int j = it->second;
+ if(ps[i].x - ps[i].y > ps[j].x - ps[j].y)break;
+ assert(ps[i].x >= ps[j].x && ps[i].y >= ps[j].y);
+ edges.push_back({(ps[i].x - ps[j].x) + (ps[i].y - ps[j].y), i, j});
+ }
+ active[ps[i].x] = i;
+ }
+ for(auto &p : ps){ // rotate
+ if(rot&1)p.x *= -1;
+ else swap(p.x, p.y);
+ }
+ }
+ return edges;
+}
+```
diff --git a/test/manhattan_mst.cpp b/test/manhattan_mst.cpp
new file mode 100644
index 000000000..c408f0833
--- /dev/null
+++ b/test/manhattan_mst.cpp
@@ -0,0 +1,71 @@
+#include
+using namespace std;
+#include "manhattan_mst.h"
+
+struct point {
+ int x, y;
+};
+
+struct DSU {
+ int n;
+ vector p, ps;
+ DSU(int _n){
+ n = _n;
+ p = ps = vector(n+1, 1);
+ for(int i=1;i<=n;i++)p[i] = i;
+ }
+ int f(int x){return p[x]=(p[x]==x?x:f(p[x]));}
+ bool join(int a,int b){
+ a=f(a),b=f(b);
+ if(a==b)return false;
+ if(ps[a] > ps[b])swap(a,b);
+ ps[b] += ps[a];
+ p[a] = b;
+ return true;
+ }
+};
+
+long long mst_cost(vector > e,int n){
+ sort(e.begin(), e.end());
+ DSU dsu(n);
+ long long c=0;
+ for(auto &[w, i, j] : e){
+ if(dsu.join(i, j))c += w;
+ }
+ return c;
+}
+
+vector > brute(vector ps){
+ vector > e;
+ for(int i=0;i get_random_points(int n,int maxC){
+ vector ps;
+ for(int i=0;i max_cs = {5, 1000, 100000, 100000000};
+ vector ns = {5, 100, 500};
+ for(int maxC : max_cs)for(int n : ns){
+ auto ps = get_random_points(n, maxC);
+ auto e1 = brute(ps);
+ auto e2 = manhattan_mst_edges(ps);
+ assert(mst_cost(e1, n) == mst_cost(e2, n));
+ }
+ auto time_begin = clock();
+ auto ps = get_random_points(200000, 1000000);
+ auto e = manhattan_mst_edges(ps);
+ cerr << setprecision(5) << fixed;
+ cerr << (double)(clock() - time_begin)/CLOCKS_PER_SEC << endl;
+ assert((double)(clock() - time_begin)/CLOCKS_PER_SEC < 2);
+}
From 905e688cd43163307d35857ad12b0e1399d92bc9 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sat, 18 May 2024 11:50:41 +0530
Subject: [PATCH 013/280] Add Knapsack subtopic
---
src/dynamic_programming/knapsack.md | 67 +++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 src/dynamic_programming/knapsack.md
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
new file mode 100644
index 000000000..f3ab10a33
--- /dev/null
+++ b/src/dynamic_programming/knapsack.md
@@ -0,0 +1,67 @@
+---
+tags:
+ - Original
+---
+
+# Knapsack DP
+Prerequisite knowledge: [Introduction to Dynamic Programming](https://cp-algorithms.com/dynamic_programming/intro-to-dp.html)
+
+## Introduction
+Before we talk about what "knapsack dp" is, let's look at the following example:
+
+### [[USACO07 Dec] Charm Bracelet](https://www.acmicpc.net/problem/6144)
+There are $n$ distinguishable items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
+It is required to select a number of items to put into the knapsack such that the total weight does not exceed the capacity and the total value is maximized.
+
+In the above example, since each object has only two possible states (taken or not taken),
+correspoding to binary 0 and 1, this type of problem is called "0-1 knapsack problem".
+
+## 0-1 Knapsack
+
+### Explaination
+
+The known conditions in the example are; the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack {W}.
+
+Set the dp state $f_{i, j}$ be the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
+
+Consider the state transfer. Assuming that all states of first $i-1$ items have been processed, for the $i^{th}$ item;
+- When it is not put into the knapsack, the remaining capacity remains unchanged and total value does not change. Therefore, the maximum value in this case is $f_{i-1, j}$
+- When it is put into the knapsack, the remaining capacity decreases by $w_{i}$ and the total value increases by $v_{i}$,
+so the maximum value in this case is $f_{i-1, j-w_i} + v_i$
+
+From this we can derive the state transfer equation:
+
+$$f_{i, j} = \max(f_{i-1, j}, f_{i-1, j-w_i} + v_i)$$
+
+Further, as $f_{i}$ is only dependent on $f_{i-1}$, we can remove the first dimension. We obtain:
+
+$$f_{j} = \max(f_{j}, f_{j-w_i} + v_i)$$
+
+**It is important to remember and understand this transfer equation, because most of the transfer equations for knapsack problems are derived on this basis.**
+
+### Implementation
+
+Another thing to note is that it is easy to write **erroneous code** like this
+
+```.c++
+for (int i = 1; i <= n; i++)
+ for (int j = 0; j <= W-w[i]; j++)
+ f[j + w[i]] = max(f[j + w[i]], f[j] + v[i]);
+```
+
+What is wrong with this code? The enumeration order is wrong.
+
+Observing the code carefully, we can find that: for the currently processed item $i$ and the current state $f_{i,j}$,
+when $j\geqslant w_{i}$, $f_{i,j}$ will be affected by $f_{i,j-w_{i}}$.
+This is equivalent to being able to be put item $i$ into the backpack multiple times, which is not consistent with the question.
+(In fact, this is exactly the solution to the complete knapsack problem)
+
+To avoid this, we can change the order of enumeration from $W$ to $w_{i}$, so that the above error won't occour, because $f_{i, j}$ is always updated before $f_{i, j-w_i}$
+
+Therefore, the actual code is
+
+```.c++
+for (int i = 1; i <= n; i++)
+ for (int j = W; j >= w[i]; j--)
+ f[j] = max(f[j], f[j - w[i]] + v[i]);
+```
From 7357f515416e956602ae6527c0897bbb9ccb607e Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sat, 18 May 2024 12:06:06 +0530
Subject: [PATCH 014/280] Update navigation.md, add knapsack.md
---
src/navigation.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/navigation.md b/src/navigation.md
index de682c536..1590d2860 100644
--- a/src/navigation.md
+++ b/src/navigation.md
@@ -62,6 +62,7 @@ search:
- [Deleting from a data structure in O(T(n) log n)](data_structures/deleting_in_log_n.md)
- Dynamic Programming
- [Introducton to Dynamic Programming](dynamic_programming/intro-to-dp.md)
+ - [Knapsack DP](dynamic_programming/knapsack.md)
- DP optimizations
- [Divide and Conquer DP](dynamic_programming/divide-and-conquer-dp.md)
- [Knuth's Optimization](dynamic_programming/knuth-optimization.md)
From 42202c3de6596189473aa4765dc2069b635407d9 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 19 May 2024 11:26:37 +0530
Subject: [PATCH 015/280] Add knapsack.md
---
src/dynamic_programming/knapsack.md | 90 +++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index f3ab10a33..3bc88630e 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -65,3 +65,93 @@ for (int i = 1; i <= n; i++)
for (int j = W; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
```
+
+## Complete Knapsack
+
+The complete knapsack model is similar to the 0-1 knapsack, the only difference from the 0-1 knapsack is that an item can be selected an unlimited number of times instead of only once.
+
+We can refer to the idea of 0-1 knapsack to define the state: $f_{i, j}$, the maximum value the knapsack can obtain using the first $i$ items with maximum capacity $j$.
+
+It should be noted that although the state definition is similar to that of a 0-1 knapsack, its state transfer equation is different from that of a 0-1 knapsack.
+
+### Explaination
+
+The trivial approach is, for the first $i$ items, enumerate how many each item is to be taken. The time complexity of this is $O(n^3)$.
+
+The state transfer function follows:
+
+$$f_{i, j} = \max\limits_{k=0}^{\infty}(f_{i-1, j-k\cdot w_i} + k\cdot v_i)$$
+
+It can be found that for $f_{i, j}$, it can just transfer through $f_{i, j-w_i}$. The state transfer equation becomes:
+
+$$f_{i, j} = \max(f_{i-1, j},f_{i, j-w_i} + v_i)$$\
+
+The reason this works is that when we transfer like this, $f_{i, j-w_i}$ has already been updated by $f_{i, j-2\cdot w_i}$ and so on.
+
+Similar to the 0-1 knapsack, we can remove the first dimension to optimize the space complexity.
+
+## Multiple Knapsack
+
+Multiple knapsack is also a variant of 0-1 knapsack. The main difference is that there are $k_i$ of each item instead of just 1.
+
+### Explaination
+
+A very simple idea is: "choose each item $k_i$ times" is equivalent to "$k_i$ of the same item is selected one by one". Thus converting it to a 0-1 knapsack model, which can be described by the transition function:
+
+$$f_{i, j} = \max_{k=0}^{k_i}(f_{i-1,j-k\cdot w_i} + k\cdot v_i)$$
+
+The time complexity of this process is $O(W\sum\limits_{i=1}^{n}k_i)$
+
+### Binary Grouping Optimization
+
+We still consider converting the multiple knapsack model into a 0-1 knapsack model for optimization. The time complexity $O(Wn)$ can not be further optimized, so we focus on $O(\sum k_i)$.
+
+Let $A_{i, j}$ denote the $j^{th}$ item split from the $i^{th}$ item. In the trivial approach discussed above, $A_{i, j}$ represents the same item for all $j \leq k_i$. The main reason for our low efficiency is that we are doing a lot of repetetive work. For example, consider selecting $A_{i, 1} and A_{i, 2}$, and selecting $A_{i, 3} and A_{i, 3}$. These two situations are completely equivalent. Thus optimizing the spiltting method will greatly reduces the time complexity.
+
+The grouping is made more effiecent by using binary grouping.
+
+Specifically, $A_{i, j}$ holds $2^j$ individual items ($j\in[0,\lfloor \log_2(k_i+1)\rfloor-1]$).If $k_i + 1$ is not an integer power of $2$, another bundle of size $k_i-2^{\lfloor \log_2(k_i+1)\rfloor-1}$ is used to make up for it.
+
+Through the above splitting method, any sum of terms $\leq k_i$ will can be obtained by selecting a few $A_{i, j}$'s. After splitting each item in the described way, it is sufficient to use 0-1 knapsack method to solve it.
+
+### Implementation
+
+```c++
+index = 0;
+for (int i = 1; i <= n; i++) {
+ int c = 1, p, h, k;
+ cin >> p >> h >> k;
+ while (k > c) {
+ k -= c;
+ list[++index].w = c * p;
+ list[index].v = c * h;
+ c *= 2;
+ }
+ list[++index].w = p * k;
+ list[index].v = h * k;
+}
+```
+
+## Mixed Knapsack
+
+The mixed knapsack problem involves a combination of the three problems described above. That is, some items can only be taken once, some can be taken infinitely, and some can be taken atmost $k$ times.
+
+The problem may seem daunting, but as long as you understand the core ideas of the previous knapsack problems and combine them together, you can do it. The pseudo code for the solution is as:
+
+```c++
+for (each item) {
+ if (0-1 knapsack)
+ Apply 0-1 knapsack code;
+ else if (complete knapsack)
+ Apply complete knapsack code;
+ else if (multiple knapsack)
+ Apply multiple knapsack code;
+}
+```
+
+## Practise Problems
+
+- [Atcoder: Knapsack-1](https://atcoder.jp/contests/dp/tasks/dp_d)
+- [Atcoder: Knapsack-2](https://atcoder.jp/contests/dp/tasks/dp_e)
+- [DMOJ: Knapsack-3](https://dmoj.ca/problem/knapsack)
+- [DMOJ: Knapsack-4](https://dmoj.ca/problem/knapsack4)
From 79d81614d800a0ed89b625e0633057a9a68ee7d1 Mon Sep 17 00:00:00 2001
From: Caio
Date: Tue, 28 May 2024 17:58:49 -0300
Subject: [PATCH 016/280] spelling mistake fix
---
src/navigation.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/navigation.md b/src/navigation.md
index de682c536..361bdbef9 100644
--- a/src/navigation.md
+++ b/src/navigation.md
@@ -61,7 +61,7 @@ search:
- Advanced
- [Deleting from a data structure in O(T(n) log n)](data_structures/deleting_in_log_n.md)
- Dynamic Programming
- - [Introducton to Dynamic Programming](dynamic_programming/intro-to-dp.md)
+ - [Introduction to Dynamic Programming](dynamic_programming/intro-to-dp.md)
- DP optimizations
- [Divide and Conquer DP](dynamic_programming/divide-and-conquer-dp.md)
- [Knuth's Optimization](dynamic_programming/knuth-optimization.md)
From fa6bd3f776075152a0588c4b87c6cefb1b2b0a94 Mon Sep 17 00:00:00 2001
From: Shubham Phapale <94707673+ShubhamPhapale@users.noreply.github.com>
Date: Fri, 17 May 2024 16:40:37 +0530
Subject: [PATCH 017/280] Added Practice Problems
---
src/dynamic_programming/intro-to-dp.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 0102581fa..7fdb6f8e5 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -150,4 +150,6 @@ Of course, the most important trick is to practice.
* [LeetCode - 1137. N-th Tribonacci Number](https://leetcode.com/problems/n-th-tribonacci-number/description/)
* [LeetCode - 118. Pascal's Triangle](https://leetcode.com/problems/pascals-triangle/description/)
* [LeetCode - 1025. Divisor Game](https://leetcode.com/problems/divisor-game/description/)
+* [Codeforces - Zuma](https://codeforces.com/problemset/problem/607/b)
+* [LeetCode - 221. Maximal Square](https://leetcode.com/problems/maximal-square/description/)
From b94c1dd55886f8f2a9edc5cbe6ef3b190749a442 Mon Sep 17 00:00:00 2001
From: Shubham Phapale <94707673+ShubhamPhapale@users.noreply.github.com>
Date: Fri, 17 May 2024 16:56:19 +0530
Subject: [PATCH 018/280] Added Practice Problems
---
src/dynamic_programming/intro-to-dp.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/dynamic_programming/intro-to-dp.md b/src/dynamic_programming/intro-to-dp.md
index 7fdb6f8e5..45f36b78f 100644
--- a/src/dynamic_programming/intro-to-dp.md
+++ b/src/dynamic_programming/intro-to-dp.md
@@ -152,4 +152,5 @@ Of course, the most important trick is to practice.
* [LeetCode - 1025. Divisor Game](https://leetcode.com/problems/divisor-game/description/)
* [Codeforces - Zuma](https://codeforces.com/problemset/problem/607/b)
* [LeetCode - 221. Maximal Square](https://leetcode.com/problems/maximal-square/description/)
+* [LeetCode - 1039. Minimum Score Triangulation of Polygon](https://leetcode.com/problems/minimum-score-triangulation-of-polygon/description/)
From 69febce59abd37722e46c0cacd74ae93a0698391 Mon Sep 17 00:00:00 2001
From: Elliot Ha
Date: Sat, 8 Jun 2024 14:18:51 -0700
Subject: [PATCH 019/280] Added proof of edge classification in undirected
graphs
The reasoning for why forward and cross edges didn't exist in undirected graphs stumped me when I was first learning graph theory from the wiki. Hoping to help bring some intuition for future students and expand upon your guys' great work!
---
src/graph/depth-first-search.md | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/graph/depth-first-search.md b/src/graph/depth-first-search.md
index efef1e79d..f65d27dd4 100644
--- a/src/graph/depth-first-search.md
+++ b/src/graph/depth-first-search.md
@@ -57,7 +57,7 @@ For more details check out the implementation.
## Classification of edges of a graph
-We can classify the edges using the entry and exit time of the end nodes $u$ and $v$ of the edges $(u,v)$.
+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)$.
These classifications are often used for problems like [finding bridges](bridge-searching.md) and [finding articulation points](cutpoints.md).
We perform a DFS and classify the encountered edges using the following rules:
@@ -74,7 +74,15 @@ If $v$ is visited before $u$:
* 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.
* 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.
-Note: Forward edges and cross edges only exist in directed graphs.
+**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.
+
+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:
+
+* 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.
+
+* 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.
+
+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.
## Implementation
From 59f42fcc5db032fcbbc1041b947e63278cc40661 Mon Sep 17 00:00:00 2001
From: Kshitij Sharma
Date: Thu, 16 May 2024 04:46:08 +0530
Subject: [PATCH 020/280] Update bridge-searching-online.md
---
src/graph/bridge-searching-online.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/graph/bridge-searching-online.md b/src/graph/bridge-searching-online.md
index 702dc5a88..80ed25ead 100644
--- a/src/graph/bridge-searching-online.md
+++ b/src/graph/bridge-searching-online.md
@@ -174,7 +174,6 @@ int find_cc(int v) {
}
void make_root(int v) {
- v = find_2ecc(v);
int root = v;
int child = -1;
while (v != -1) {
From b608a6007be8ce8f7f8ea8b0524c7aadf81ba0fa Mon Sep 17 00:00:00 2001
From: Oleksandr Kulkov
Date: Sat, 8 Jun 2024 23:58:23 +0200
Subject: [PATCH 021/280] Update test_bridge_searching_offline.cpp
sort -> std::sort
---
test/test_bridge_searching_offline.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_bridge_searching_offline.cpp b/test/test_bridge_searching_offline.cpp
index 8ca73fba0..32d24239e 100644
--- a/test/test_bridge_searching_offline.cpp
+++ b/test/test_bridge_searching_offline.cpp
@@ -14,7 +14,7 @@ void normalize(vector> &v){
for(auto &p : v){
if(p.first > p.second)swap(p.first, p.second);
}
- sort(v.begin(), v.end());
+ std::sort(v.begin(), v.end());
}
void test_suite(
From 8f8c4f8f69ab5a022ea65e73e46afa9be1d0bf6a Mon Sep 17 00:00:00 2001
From: Oleksandr Kulkov
Date: Sat, 8 Jun 2024 23:59:26 +0200
Subject: [PATCH 022/280] Update test_bridge_searching_offline.cpp
include
---
test/test_bridge_searching_offline.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/test_bridge_searching_offline.cpp b/test/test_bridge_searching_offline.cpp
index 32d24239e..f02371881 100644
--- a/test/test_bridge_searching_offline.cpp
+++ b/test/test_bridge_searching_offline.cpp
@@ -1,3 +1,4 @@
+#include
#include
#include
#include
@@ -14,7 +15,7 @@ void normalize(vector> &v){
for(auto &p : v){
if(p.first > p.second)swap(p.first, p.second);
}
- std::sort(v.begin(), v.end());
+ sort(v.begin(), v.end());
}
void test_suite(
From ff4b38046067c1e90975db144af8cd303c69ce21 Mon Sep 17 00:00:00 2001
From: Oleksandr Kulkov
Date: Sun, 9 Jun 2024 02:06:32 +0200
Subject: [PATCH 023/280] Touch for CI
From 2a1dc126c12c1c2dcc31c8685dbb38c92356ecd3 Mon Sep 17 00:00:00 2001
From: Oleksandr Kulkov
Date: Sun, 9 Jun 2024 02:14:23 +0200
Subject: [PATCH 024/280] Remove CCOMPS (can't be submitted)
---
src/graph/search-for-connected-components.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/graph/search-for-connected-components.md b/src/graph/search-for-connected-components.md
index e96391d11..cfc0f27ac 100644
--- a/src/graph/search-for-connected-components.md
+++ b/src/graph/search-for-connected-components.md
@@ -102,7 +102,6 @@ void find_comps() {
```
## Practice Problems
- - [SPOJ: CCOMPS](http://www.spoj.com/problems/CCOMPS/)
- [SPOJ: CT23E](http://www.spoj.com/problems/CT23E/)
- [CODECHEF: GERALD07](https://www.codechef.com/MARCH14/problems/GERALD07)
- [CSES : Building Roads](https://cses.fi/problemset/task/1666)
From 54fa080ed34370ea355aa8c010a12e2ac0cee0da Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:03:36 +0530
Subject: [PATCH 025/280] Update README.md, add knapsack dp
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 2aeaa5d77..72ec3bee7 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ Compiled pages are published at [https://cp-algorithms.com/](https://cp-algorith
### New articles
+- (8 June 2024) [Knapsack Problem](https://cp-algorithms.com/dynamic_programming/knapsack.html)
- (28 January 2024) [Introduction to Dynamic Programming](https://cp-algorithms.com/dynamic_programming/intro-to-dp.html)
- (8 December 2023) [Hungarian Algorithm](https://cp-algorithms.com/graph/hungarian-algorithm.html)
- (10 September 2023) [Tortoise and Hare Algorithm](https://cp-algorithms.com/others/tortoise_and_hare.html)
From af311ab9ff02ecffb9e24011ea882667cba8138c Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:06:11 +0530
Subject: [PATCH 026/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 3bc88630e..78dc315fd 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -3,7 +3,7 @@ tags:
- Original
---
-# Knapsack DP
+# Knapsack problem
Prerequisite knowledge: [Introduction to Dynamic Programming](https://cp-algorithms.com/dynamic_programming/intro-to-dp.html)
## Introduction
From d78306c5d8f25f5a51278606fdead44a47cc719f Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:06:28 +0530
Subject: [PATCH 027/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 78dc315fd..4dfee5403 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -7,7 +7,7 @@ tags:
Prerequisite knowledge: [Introduction to Dynamic Programming](https://cp-algorithms.com/dynamic_programming/intro-to-dp.html)
## Introduction
-Before we talk about what "knapsack dp" is, let's look at the following example:
+Consider the following example:
### [[USACO07 Dec] Charm Bracelet](https://www.acmicpc.net/problem/6144)
There are $n$ distinguishable items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
From ac205c34def7614359b5f4d4fca46a3717c216e7 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:06:48 +0530
Subject: [PATCH 028/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 4dfee5403..3d51b2add 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -10,7 +10,7 @@ Prerequisite knowledge: [Introduction to Dynamic Programming](https://cp-algorit
Consider the following example:
### [[USACO07 Dec] Charm Bracelet](https://www.acmicpc.net/problem/6144)
-There are $n$ distinguishable items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
+There are $n$ distinct items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
It is required to select a number of items to put into the knapsack such that the total weight does not exceed the capacity and the total value is maximized.
In the above example, since each object has only two possible states (taken or not taken),
From 142c3cce22cdc6e8b952ea71389cef3a00848870 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:07:08 +0530
Subject: [PATCH 029/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 3d51b2add..13d6665a4 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -11,7 +11,7 @@ Consider the following example:
### [[USACO07 Dec] Charm Bracelet](https://www.acmicpc.net/problem/6144)
There are $n$ distinct items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
-It is required to select a number of items to put into the knapsack such that the total weight does not exceed the capacity and the total value is maximized.
+You have to select a subset of items to put into the knapsack such that the total weight does not exceed the capacity $W$ and the total value is maximized.
In the above example, since each object has only two possible states (taken or not taken),
correspoding to binary 0 and 1, this type of problem is called "0-1 knapsack problem".
From 29fe30bd2155ff069ff11ae2ec5c8f35d74f3fa5 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:07:21 +0530
Subject: [PATCH 030/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 13d6665a4..5f1c9d7b1 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -13,7 +13,7 @@ Consider the following example:
There are $n$ distinct items and a knapsack of capacity $W$. Each item has 2 attributes, weight ($w_{i}$) and value ($v_{i}$).
You have to select a subset of items to put into the knapsack such that the total weight does not exceed the capacity $W$ and the total value is maximized.
-In the above example, since each object has only two possible states (taken or not taken),
+In the example above, since each object has only two possible states (taken or not taken),
correspoding to binary 0 and 1, this type of problem is called "0-1 knapsack problem".
## 0-1 Knapsack
From af72957812bc7673266c39d283631c5d8920f61d Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:07:53 +0530
Subject: [PATCH 031/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 5f1c9d7b1..2e6498831 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -14,7 +14,7 @@ There are $n$ distinct items and a knapsack of capacity $W$. Each item has 2 att
You have to select a subset of items to put into the knapsack such that the total weight does not exceed the capacity $W$ and the total value is maximized.
In the example above, since each object has only two possible states (taken or not taken),
-correspoding to binary 0 and 1, this type of problem is called "0-1 knapsack problem".
+correspoding to binary 0 and 1. Thus, this type of problem is called "0-1 knapsack problem".
## 0-1 Knapsack
From 1c5bc0076207aff933f0419205b477a4aed58dbb Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:08:13 +0530
Subject: [PATCH 032/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 2e6498831..af39f26df 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -20,7 +20,7 @@ correspoding to binary 0 and 1. Thus, this type of problem is called "0-1 knapsa
### Explaination
-The known conditions in the example are; the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack {W}.
+In the example above, the input to the problem is the following: the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack {W}.
Set the dp state $f_{i, j}$ be the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
From 0df0b911a50acc75882678fe31952a5cf81b9b93 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:08:39 +0530
Subject: [PATCH 033/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index af39f26df..9fcac37b8 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -22,7 +22,7 @@ correspoding to binary 0 and 1. Thus, this type of problem is called "0-1 knapsa
In the example above, the input to the problem is the following: the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack {W}.
-Set the dp state $f_{i, j}$ be the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
+Let $f_{i, j}$ be the dynamic programming state holding the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
Consider the state transfer. Assuming that all states of first $i-1$ items have been processed, for the $i^{th}$ item;
- When it is not put into the knapsack, the remaining capacity remains unchanged and total value does not change. Therefore, the maximum value in this case is $f_{i-1, j}$
From 36544e2128aa792f160a53abcd90d7f572baae65 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:09:18 +0530
Subject: [PATCH 034/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 9fcac37b8..203b0c556 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -24,7 +24,7 @@ In the example above, the input to the problem is the following: the weight of $
Let $f_{i, j}$ be the dynamic programming state holding the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
-Consider the state transfer. Assuming that all states of first $i-1$ items have been processed, for the $i^{th}$ item;
+Assuming that all states of the first $i-1$ items have been processed, what are the options for the $i^{th}$ item?
- When it is not put into the knapsack, the remaining capacity remains unchanged and total value does not change. Therefore, the maximum value in this case is $f_{i-1, j}$
- When it is put into the knapsack, the remaining capacity decreases by $w_{i}$ and the total value increases by $v_{i}$,
so the maximum value in this case is $f_{i-1, j-w_i} + v_i$
From 7ca6d610c9028677e0a591b144f3c7ba35982c9d Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:09:55 +0530
Subject: [PATCH 035/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 203b0c556..3fa818674 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -25,6 +25,7 @@ In the example above, the input to the problem is the following: the weight of $
Let $f_{i, j}$ be the dynamic programming state holding the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
Assuming that all states of the first $i-1$ items have been processed, what are the options for the $i^{th}$ item?
+
- When it is not put into the knapsack, the remaining capacity remains unchanged and total value does not change. Therefore, the maximum value in this case is $f_{i-1, j}$
- When it is put into the knapsack, the remaining capacity decreases by $w_{i}$ and the total value increases by $v_{i}$,
so the maximum value in this case is $f_{i-1, j-w_i} + v_i$
From 38b0563a1e3f82c8f6b84a0ae2a83bac7bdebf11 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:10:06 +0530
Subject: [PATCH 036/280] Update src/dynamic_programming/knapsack.md
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 3fa818674..350f9f4d3 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -30,7 +30,7 @@ Assuming that all states of the first $i-1$ items have been processed, what are
- When it is put into the knapsack, the remaining capacity decreases by $w_{i}$ and the total value increases by $v_{i}$,
so the maximum value in this case is $f_{i-1, j-w_i} + v_i$
-From this we can derive the state transfer equation:
+From this we can derive the dp transition equation:
$$f_{i, j} = \max(f_{i-1, j}, f_{i-1, j-w_i} + v_i)$$
From 3d2f59cae42ec8f583cbf12e68518689ea5c9a6c Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 16:16:36 +0530
Subject: [PATCH 037/280] Apply suggestions from code review
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 350f9f4d3..f7b85a3de 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -36,13 +36,13 @@ $$f_{i, j} = \max(f_{i-1, j}, f_{i-1, j-w_i} + v_i)$$
Further, as $f_{i}$ is only dependent on $f_{i-1}$, we can remove the first dimension. We obtain:
-$$f_{j} = \max(f_{j}, f_{j-w_i} + v_i)$$
+$$f_j \gets \max(f_j, f_{j-w_i}+v_i)$$
-**It is important to remember and understand this transfer equation, because most of the transfer equations for knapsack problems are derived on this basis.**
+**It is important to understand this transition rule, because most of the transitions for knapsack problems are derived in a similar way.**
### Implementation
-Another thing to note is that it is easy to write **erroneous code** like this
+It may be tempting to write **erroneous code** as follows:
```.c++
for (int i = 1; i <= n; i++)
@@ -50,14 +50,14 @@ for (int i = 1; i <= n; i++)
f[j + w[i]] = max(f[j + w[i]], f[j] + v[i]);
```
-What is wrong with this code? The enumeration order is wrong.
+The problem of the code above is the wrong execution order.
-Observing the code carefully, we can find that: for the currently processed item $i$ and the current state $f_{i,j}$,
+Observing the code carefully, we see that for the currently processed item $i$ and the current state $f_{i,j}$,
when $j\geqslant w_{i}$, $f_{i,j}$ will be affected by $f_{i,j-w_{i}}$.
-This is equivalent to being able to be put item $i$ into the backpack multiple times, which is not consistent with the question.
+This is equivalent to being able to put item $i$ into the backpack multiple times, which is not consistent with the question.
(In fact, this is exactly the solution to the complete knapsack problem)
-To avoid this, we can change the order of enumeration from $W$ to $w_{i}$, so that the above error won't occour, because $f_{i, j}$ is always updated before $f_{i, j-w_i}$
+To avoid this, we can change the order of execution to go over $j$ from $W$ to $w_{i}$, so that the above error won't occour, because $f_{i, j}$ is always implicitly updated before $f_{i, j-w_i}$
Therefore, the actual code is
@@ -73,27 +73,27 @@ The complete knapsack model is similar to the 0-1 knapsack, the only difference
We can refer to the idea of 0-1 knapsack to define the state: $f_{i, j}$, the maximum value the knapsack can obtain using the first $i$ items with maximum capacity $j$.
-It should be noted that although the state definition is similar to that of a 0-1 knapsack, its state transfer equation is different from that of a 0-1 knapsack.
+It should be noted that although the state definition is similar to that of a 0-1 knapsack, its transition rule is different from that of a 0-1 knapsack.
### Explaination
The trivial approach is, for the first $i$ items, enumerate how many each item is to be taken. The time complexity of this is $O(n^3)$.
-The state transfer function follows:
+This yields the following transition equation:
$$f_{i, j} = \max\limits_{k=0}^{\infty}(f_{i-1, j-k\cdot w_i} + k\cdot v_i)$$
It can be found that for $f_{i, j}$, it can just transfer through $f_{i, j-w_i}$. The state transfer equation becomes:
-$$f_{i, j} = \max(f_{i-1, j},f_{i, j-w_i} + v_i)$$\
+$$f_{i, j} = \max(f_{i-1, j},f_{i, j-w_i} + v_i)$$
-The reason this works is that when we transfer like this, $f_{i, j-w_i}$ has already been updated by $f_{i, j-2\cdot w_i}$ and so on.
+The reason this works is that $f_{i, j-w_i}$ has already been updated by $f_{i, j-2\cdot w_i}$ and so on.
Similar to the 0-1 knapsack, we can remove the first dimension to optimize the space complexity.
## Multiple Knapsack
-Multiple knapsack is also a variant of 0-1 knapsack. The main difference is that there are $k_i$ of each item instead of just 1.
+Multiple knapsack is also a variant of 0-1 knapsack. The main difference is that there are $k_i$ of each item instead of just $1$.
### Explaination
@@ -107,7 +107,7 @@ The time complexity of this process is $O(W\sum\limits_{i=1}^{n}k_i)$
We still consider converting the multiple knapsack model into a 0-1 knapsack model for optimization. The time complexity $O(Wn)$ can not be further optimized, so we focus on $O(\sum k_i)$.
-Let $A_{i, j}$ denote the $j^{th}$ item split from the $i^{th}$ item. In the trivial approach discussed above, $A_{i, j}$ represents the same item for all $j \leq k_i$. The main reason for our low efficiency is that we are doing a lot of repetetive work. For example, consider selecting $A_{i, 1} and A_{i, 2}$, and selecting $A_{i, 3} and A_{i, 3}$. These two situations are completely equivalent. Thus optimizing the spiltting method will greatly reduces the time complexity.
+Let $A_{i, j}$ denote the $j^{th}$ item split from the $i^{th}$ item. In the trivial approach discussed above, $A_{i, j}$ represents the same item for all $j \leq k_i$. The main reason for our low efficiency is that we are doing a lot of repetetive work. For example, consider selecting $\{A_{i, 1},A_{i, 2}\}$, and selecting $\{A_{i, 3}, A_{i, 3}\}$. These two situations are completely equivalent. Thus optimizing the spiltting method will greatly reduce the time complexity.
The grouping is made more effiecent by using binary grouping.
From de625bfecc5f2fc266711e84f3cf4d135a832bea Mon Sep 17 00:00:00 2001
From: NaimSS
Date: Sun, 9 Jun 2024 15:33:53 +0200
Subject: [PATCH 038/280] fix typos
---
src/geometry/manhattan-distance.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md
index f0c5eed8f..7df382aee 100644
--- a/src/geometry/manhattan-distance.md
+++ b/src/geometry/manhattan-distance.md
@@ -39,22 +39,22 @@ As we made $p$ and $q$ independent, it is now easy to find the $p$ and $q$ that
## Manhattan Minimum Spanning Tree
-The Manhattan MST problem consists of, given some points in the plane, find the edges that connect all the points and have a minimum total sum of weights. The weight of an edge that connects to points is their Manhattan distance. For simplicity, we assume that all points have different locations.
-Here we show a way of finding the MST in $O(n\logn)$ by finding for each point its nearest neighbor in each octant, as represented by the image below. This will give us $O(n)$ candidate edges, which will guarantee that they contain the MST. The final step is then using some standard MST, for example, [Kruskal algorithm using disjoint set union](https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html).
+The Manhattan MST problem consists of, given some points in the plane, find the edges that connect all the points and have a minimum total sum of weights. The weight of an edge that connects two points is their Manhattan distance. For simplicity, we assume that all points have different locations.
+Here we show a way of finding the MST in $O(n \log{n})$ by finding for each point its nearest neighbor in each octant, as represented by the image below. This will give us $O(n)$ candidate edges, which will guarantee that they contain the MST. The final step is then using some standard MST, for example, [Kruskal algorithm using disjoint set union](https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html).
-The algorithm show here was first presented in a paper from [H. Zhou, N. Shenoy, and W. Nichollos (2002)](https://ieeexplore.ieee.org/document/913303). There is also another know algorithm that uses a Divide and conquer approach by [J. Stolfi](https://www.academia.edu/15667173/On_computing_all_north_east_nearest_neighbors_in_the_L1_metric), which is also very interesting and only differ in the way they find the nearest neighbor in each octant.
+The algorithm show here was first presented in a paper from [H. Zhou, N. Shenoy, and W. Nichollos (2002)](https://ieeexplore.ieee.org/document/913303). There is also another know algorithm that uses a Divide and conquer approach by [J. Stolfi](https://www.academia.edu/15667173/On_computing_all_north_east_nearest_neighbors_in_the_L1_metric), which is also very interesting and only differ in the way they find the nearest neighbor in each octant. They both have the same complexity, but the one presented here is easier to implement and has a lower constant factor.
-First, let's understand why it is enough to consider only the nearest neighbor in each octant. The idea is to show that for a point s and any two other points $p$ and $q$ in the same octant, $dist(p, q) < max(dist(s, p), dist(s, q))$. This is important, because it shows that if there was a MST where $s$ is connected to both $p$ and $q$, we could erase one of these edges and add the edge $(p,q)$, which would decrease the total cost. To prove, we assume without loss of generality that $p$ and $q$ are in the octanct $R_1$, which is defined by: $x_s \leq x$ and $x_s - y_s > x - y$, and then do some casework. The images below give some intuition on why this is true.
+First, let's understand why it is enough to consider only the nearest neighbor in each octant. The idea is to show that for a point $s$ and any two other points $p$ and $q$ in the same octant, $dist(p, q) < max(dist(s, p), dist(s, q))$. This is important, because it shows that if there was a MST where $s$ is connected to both $p$ and $q$, we could erase one of these edges and add the edge $(p,q)$, which would decrease the total cost. To prove this, we assume without loss of generality that $p$ and $q$ are in the octanct $R_1$, which is defined by: $x_s \leq x$ and $x_s - y_s > x - y$, and then do some casework. The images below give some intuition on why this is true.
Therefore, the main question is how to find the nearest neighbor in each octant for every single of the $n$ points.
-## Nearest Neighbor in each Octant in $O(n\logn)$
+## Nearest Neighbor in each Octant in $O(n\log{n})$
For simplicity we focus on the north-east octant. All other directions can be found with the same algorithm by rotating the input.
-We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet.
+We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet, which we call "active set".
-When we add a new point point $p$, for every point $s$ that has it in it's octant we can safely assign $p$ as the nearest neighbor. This is true because their distance is $d(p,s) = |x_p - x_s| + |y_p - y_s| = (x_p + y_p) - (x_s + y_s)$, because $p$ is in the north-east octant. As all the next points will not have a smaller value of $x + y$ because of the process order, $p$ is guaranteed to have the smaller distance. We can then remove all such points from the active set, and finally add $p$ to this set.
+When we add a new point point $p$, for every point $s$ that has it in it's octant we can safely assign $p$ as the nearest neighbor. This is true because their distance is $d(p,s) = |x_p - x_s| + |y_p - y_s| = (x_p + y_p) - (x_s + y_s)$, because $p$ is in the north-east octant. As all the next points will not have a smaller value of $x + y$ because of the sorting step, $p$ is guaranteed to have the smaller distance. We can then remove all such points from the active set, and finally add $p$ to the active set.
The next question is how to efficiently find which points $s$ have $p$ in the north-east octant. That is, which points $s$ satisfy:
From a45e8080283a0e32325f5b32fe7f2b5cde7f7df4 Mon Sep 17 00:00:00 2001
From: Daniel Paleka
Date: Sun, 9 Jun 2024 16:05:49 +0200
Subject: [PATCH 039/280] add example usage, fix var names, descriptions on
tests
---
src/graph/2SAT.md | 37 ++++++++++++++++----------
test/test_2sat.cpp | 65 ++++++++++++++++++++++------------------------
2 files changed, 54 insertions(+), 48 deletions(-)
diff --git a/src/graph/2SAT.md b/src/graph/2SAT.md
index e46d245f2..5c41177d2 100644
--- a/src/graph/2SAT.md
+++ b/src/graph/2SAT.md
@@ -103,17 +103,16 @@ In the graph the vertices with indices $2k$ and $2k+1$ are the two vertices corr
```{.cpp file=2sat}
struct TwoSatSolver {
- int n;
+ int n_vars;
+ int n_vertices;
vector> adj, adj_t;
vector used;
vector order, comp;
vector assignment;
- // n is the number of variables
- TwoSatSolver(int n) : n(n), adj(2 * n), adj_t(2 * n), used(2 * n), order(), comp(2 * n, -1), assignment(n) {
- order.reserve(2 * n);
+ TwoSatSolver(int _n_vars) : n_vars(_n_vars), n_vertices(2 * n_vars), adj(n_vertices), adj_t(n_vertices), used(n_vertices), order(), comp(n_vertices, -1), assignment(n_vars) {
+ order.reserve(n_vertices);
}
-
void dfs1(int v) {
used[v] = true;
for (int u : adj[v]) {
@@ -131,24 +130,23 @@ struct TwoSatSolver {
}
}
- // m will be the number of vertices in the graph (= 2 * n)
- bool solve_2SAT(int m) {
+ bool solve_2SAT() {
order.clear();
- used.assign(m, false);
- for (int i = 0; i < m; ++i) {
+ used.assign(n_vertices, false);
+ for (int i = 0; i < n_vertices; ++i) {
if (!used[i])
dfs1(i);
}
- comp.assign(m, -1);
- for (int i = 0, j = 0; i < m; ++i) {
- int v = order[m - i - 1];
+ comp.assign(n_vertices, -1);
+ for (int i = 0, j = 0; i < n_vertices; ++i) {
+ int v = order[n_vertices - i - 1];
if (comp[v] == -1)
dfs2(v, j++);
}
- assignment.assign(m / 2, false);
- for (int i = 0; i < m; i += 2) {
+ assignment.assign(n_vars, false);
+ for (int i = 0; i < n_vertices; i += 2) {
if (comp[i] == comp[i + 1])
return false;
assignment[i / 2] = comp[i] > comp[i + 1];
@@ -167,6 +165,17 @@ struct TwoSatSolver {
adj_t[b].push_back(neg_a);
adj_t[a].push_back(neg_b);
}
+
+ static void example_usage() {
+ TwoSatSolver solver(3); // a, b, c
+ solver.add_disjunction(0, false, 1, true); // a v not b
+ solver.add_disjunction(0, true, 1, true); // not a v not b
+ solver.add_disjunction(1, false, 2, false); // b v c
+ solver.add_disjunction(0, false, 0, false); // a v a
+ assert(solver.solve_2SAT() == true);
+ auto expected = vector{{true, false, true}};
+ assert(solver.assignment == expected);
+ }
};
```
diff --git a/test/test_2sat.cpp b/test/test_2sat.cpp
index 1aa128da1..f764d2a68 100644
--- a/test/test_2sat.cpp
+++ b/test/test_2sat.cpp
@@ -4,50 +4,47 @@ using namespace std;
#include "2sat.h"
-void setup(int size) {
- n = 2 * size;
- adj.clear();
- adj.resize(n);
- adj_t.clear();
- adj_t.resize(n);
+void test_2sat_example_usage() {
+ TwoSatSolver::example_usage();
}
void test_2sat_article_example() {
- setup(3);
- add_disjunction(0, 0, 1, 1); // a v not b
- add_disjunction(0, 1, 1, 0); // not a v b
- add_disjunction(0, 1, 1, 1); // not a v not b
- add_disjunction(0, 0, 2, 1); // a v not c
- assert(solve_2SAT() == true);
- auto expected = vector{{false, false, false}};
- assert(assignment == expected);
+ TwoSatSolver solver(3); // a, b, c
+ solver.add_disjunction(0, false, 1, true); // a v not b
+ solver.add_disjunction(0, true, 1, false); // not a v b
+ solver.add_disjunction(0, true, 1, true); // not a v not b
+ solver.add_disjunction(0, false, 2, true); // a v not c
+ assert(solver.solve_2SAT() == true);
+ auto expected = vector{{false, false, false}};
+ assert(solver.assignment == expected);
}
void test_2sat_unsatisfiable() {
- setup(2);
- add_disjunction(0, 0, 1, 0); // x v y
- add_disjunction(0, 0, 1, 1); // x v not y
- add_disjunction(0, 1, 1, 0); // not x v y
- add_disjunction(0, 1, 1, 1); // not x v not y
- assert(solve_2SAT() == false);
+ TwoSatSolver solver(2); // a, b
+ solver.add_disjunction(0, false, 1, false); // a v b
+ solver.add_disjunction(0, false, 1, true); // a v not b
+ solver.add_disjunction(0, true, 1, false); // not a v b
+ solver.add_disjunction(0, true, 1, true); // not a v not b
+ assert(solver.solve_2SAT() == false);
}
void test_2sat_other_satisfiable_example() {
- setup(4);
- add_disjunction(0, 0, 1, 1); // a v not b
- add_disjunction(0, 1, 2, 1); // not a v not c
- add_disjunction(0, 0, 1, 0); // a v b
- add_disjunction(3, 0, 2, 1); // d v not c
- add_disjunction(3, 0, 0, 1); // d v not a
- assert(solve_2SAT() == true);
- // two solutions
- auto expected_1 = vector{{true, true, false, true}};
- auto expected_2 = vector{{true, false, false, true}};
- assert(assignment == expected_1 || assignment == expected_2);
+ TwoSatSolver solver(4); // a, b, c, d
+ solver.add_disjunction(0, false, 1, true); // a v not b
+ solver.add_disjunction(0, true, 2, true); // not a v not c
+ solver.add_disjunction(0, false, 1, false); // a v b
+ solver.add_disjunction(3, false, 2, true); // d v not c
+ solver.add_disjunction(3, false, 0, true); // d v not a
+ assert(solver.solve_2SAT() == true);
+ // two solutions
+ auto expected_1 = vector{{true, true, false, true}};
+ auto expected_2 = vector{{true, false, false, true}};
+ assert(solver.assignment == expected_1 || solver.assignment == expected_2);
}
int main() {
- test_2sat_article_example();
- test_2sat_unsatisfiable();
- test_2sat_other_satisfiable_example();
+ test_2sat_example_usage();
+ test_2sat_article_example();
+ test_2sat_unsatisfiable();
+ test_2sat_other_satisfiable_example();
}
From 19fb908685b22acedeeb00bb354c23898cdaa628 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 20:48:12 +0530
Subject: [PATCH 040/280] Apply suggestions from code review
Co-authored-by: Oleksandr Kulkov
---
src/dynamic_programming/knapsack.md | 4 ++--
src/navigation.md | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index f7b85a3de..6abcfb261 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -83,7 +83,7 @@ This yields the following transition equation:
$$f_{i, j} = \max\limits_{k=0}^{\infty}(f_{i-1, j-k\cdot w_i} + k\cdot v_i)$$
-It can be found that for $f_{i, j}$, it can just transfer through $f_{i, j-w_i}$. The state transfer equation becomes:
+At the same time, it simplifies into a "flat" equation:
$$f_{i, j} = \max(f_{i-1, j},f_{i, j-w_i} + v_i)$$
@@ -105,7 +105,7 @@ The time complexity of this process is $O(W\sum\limits_{i=1}^{n}k_i)$
### Binary Grouping Optimization
-We still consider converting the multiple knapsack model into a 0-1 knapsack model for optimization. The time complexity $O(Wn)$ can not be further optimized, so we focus on $O(\sum k_i)$.
+We still consider converting the multiple knapsack model into a 0-1 knapsack model for optimization. The time complexity $O(Wn)$ can not be further optimized with the approach above, so we focus on $O(\sum k_i)$ component.
Let $A_{i, j}$ denote the $j^{th}$ item split from the $i^{th}$ item. In the trivial approach discussed above, $A_{i, j}$ represents the same item for all $j \leq k_i$. The main reason for our low efficiency is that we are doing a lot of repetetive work. For example, consider selecting $\{A_{i, 1},A_{i, 2}\}$, and selecting $\{A_{i, 3}, A_{i, 3}\}$. These two situations are completely equivalent. Thus optimizing the spiltting method will greatly reduce the time complexity.
diff --git a/src/navigation.md b/src/navigation.md
index 0aec6cf4a..d63ebe0ec 100644
--- a/src/navigation.md
+++ b/src/navigation.md
@@ -62,7 +62,7 @@ search:
- [Deleting from a data structure in O(T(n) log n)](data_structures/deleting_in_log_n.md)
- Dynamic Programming
- [Introduction to Dynamic Programming](dynamic_programming/intro-to-dp.md)
- - [Knapsack DP](dynamic_programming/knapsack.md)
+ - [Knapsack problem](dynamic_programming/knapsack.md)
- DP optimizations
- [Divide and Conquer DP](dynamic_programming/divide-and-conquer-dp.md)
- [Knuth's Optimization](dynamic_programming/knuth-optimization.md)
From de1237d458a3f08f28e27b7e0506bef2c2401894 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Sun, 9 Jun 2024 21:59:13 +0530
Subject: [PATCH 041/280] Update knapsack.md
---
src/dynamic_programming/knapsack.md | 45 ++++++++++++++++-------------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 6abcfb261..5c3e1526b 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -42,24 +42,7 @@ $$f_j \gets \max(f_j, f_{j-w_i}+v_i)$$
### Implementation
-It may be tempting to write **erroneous code** as follows:
-
-```.c++
-for (int i = 1; i <= n; i++)
- for (int j = 0; j <= W-w[i]; j++)
- f[j + w[i]] = max(f[j + w[i]], f[j] + v[i]);
-```
-
-The problem of the code above is the wrong execution order.
-
-Observing the code carefully, we see that for the currently processed item $i$ and the current state $f_{i,j}$,
-when $j\geqslant w_{i}$, $f_{i,j}$ will be affected by $f_{i,j-w_{i}}$.
-This is equivalent to being able to put item $i$ into the backpack multiple times, which is not consistent with the question.
-(In fact, this is exactly the solution to the complete knapsack problem)
-
-To avoid this, we can change the order of execution to go over $j$ from $W$ to $w_{i}$, so that the above error won't occour, because $f_{i, j}$ is always implicitly updated before $f_{i, j-w_i}$
-
-Therefore, the actual code is
+The alorithm described can be implemented in $O(nW)$ as:
```.c++
for (int i = 1; i <= n; i++)
@@ -67,6 +50,8 @@ for (int i = 1; i <= n; i++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
```
+Note the order of enumeration. We go over all possible weights for each item rather than the other way round. If we execute the loops in the other order, $f_W$ will get updated assuming that we can pick only one item.
+
## Complete Knapsack
The complete knapsack model is similar to the 0-1 knapsack, the only difference from the 0-1 knapsack is that an item can be selected an unlimited number of times instead of only once.
@@ -77,7 +62,7 @@ It should be noted that although the state definition is similar to that of a 0-
### Explaination
-The trivial approach is, for the first $i$ items, enumerate how many each item is to be taken. The time complexity of this is $O(n^3)$.
+The trivial approach is, for the first $i$ items, enumerate how many each item is to be taken. The time complexity of this is $O(n^2W)$.
This yields the following transition equation:
@@ -89,7 +74,25 @@ $$f_{i, j} = \max(f_{i-1, j},f_{i, j-w_i} + v_i)$$
The reason this works is that $f_{i, j-w_i}$ has already been updated by $f_{i, j-2\cdot w_i}$ and so on.
-Similar to the 0-1 knapsack, we can remove the first dimension to optimize the space complexity.
+Similar to the 0-1 knapsack, we can remove the first dimension to optimize the space complexity. This gives us the same transition rule as 0-1 knapsack.
+
+$$f_j \gets \max(f_j, f_{j-w_i}+v_i)$$
+
+### Implementation
+
+The alorithm described can be implemented in $O(nW)$ as:
+
+```.c++
+for (int i = 1; i <= n; i++)
+ for (int j = 0; j <= W-w[i]; j++)
+ f[j + w[i]] = max(f[j + w[i]], f[j] + v[i]);
+```
+
+Despite the same transfer rule as 0-1 knapsack, the code above is incorrect for it.
+
+Observing the code carefully, we see that for the currently processed item $i$ and the current state $f_{i,j}$,
+when $j\geqslant w_{i}$, $f_{i,j}$ will be affected by $f_{i,j-w_{i}}$.
+This is equivalent to being able to put item $i$ into the backpack multiple times, which is consistent with the complete knapsack problem and not the 0-1 knapsack problem.
## Multiple Knapsack
@@ -115,6 +118,8 @@ Specifically, $A_{i, j}$ holds $2^j$ individual items ($j\in[0,\lfloor \log_2(k_
Through the above splitting method, any sum of terms $\leq k_i$ will can be obtained by selecting a few $A_{i, j}$'s. After splitting each item in the described way, it is sufficient to use 0-1 knapsack method to solve it.
+This optimization gives us a time complexity of $O(W\sum\limits_{i=1}^{n}\log k_i)$.
+
### Implementation
```c++
From bf340e309b3ecdd5fd7960876f564d87c9d5ad40 Mon Sep 17 00:00:00 2001
From: Akshat Nagpal <69424903+OverRancid@users.noreply.github.com>
Date: Mon, 10 Jun 2024 10:08:14 +0530
Subject: [PATCH 042/280] Update knapsack.md
---
src/dynamic_programming/knapsack.md | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/dynamic_programming/knapsack.md b/src/dynamic_programming/knapsack.md
index 5c3e1526b..3ab66e96a 100644
--- a/src/dynamic_programming/knapsack.md
+++ b/src/dynamic_programming/knapsack.md
@@ -20,7 +20,7 @@ correspoding to binary 0 and 1. Thus, this type of problem is called "0-1 knapsa
### Explaination
-In the example above, the input to the problem is the following: the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack {W}.
+In the example above, the input to the problem is the following: the weight of $i^{th}$ item $w_{i}$, value of $i^{th}$ item $v_{i}$, and the total capacity of the knapsack $W$.
Let $f_{i, j}$ be the dynamic programming state holding the maximum total value the knapsack can carry with capacity $j$, when only the first $i$ items are considered.
@@ -138,6 +138,22 @@ for (int i = 1; i <= n; i++) {
}
```
+### Monotone Queue Optimization
+
+In this optimization, we aim to convert the knapsack problem into a [maximum queue](https://cp-algorithms.com/data_structures/stack_queue_modification.html) one.
+
+For convenience of description, let $g_{x, y} = f_{i, x \cdot w_i + y} ,\space g'_{x, y} = f_{i-1, x \cdot w_i + y}$. Then the transition rule can be written as:
+
+$$g_{x, y} = \max_{k=0}^{k_i}(g'_{x-k, y} + v_i \cdot k)$$
+
+Further, let $G_{x, y} = g'_{x, y} - v_i \cdot x$. Then the transition rile can be expressed as:
+
+$$g_{x, u} \gets \max_{k=0}^{k_i}(G_{x-k, y}) + v_i \cdot x$$
+
+This transforms into a classic monotone queue optimization form. $G_{x, y}$ can be calculated in $O(1)$, so for a fixed $y$, we can calculate $g_{x, y}$ in $O(\lfloor \frac{W}{w_i} \rfloor)$ time.
+Therefore, the complexity of finding all $g_{x, y}$ is $O(\lfloor \frac{W}{w_i} \rfloor) \times O(w_i) = O(W)$.
+In this way, the total complexity of the transfer is reduced to $O(nW)$.
+
## Mixed Knapsack
The mixed knapsack problem involves a combination of the three problems described above. That is, some items can only be taken once, some can be taken infinitely, and some can be taken atmost $k$ times.
From 9753e49855708b5141dbb94647d7fd80266578ce Mon Sep 17 00:00:00 2001
From: Nadezhda R
Date: Mon, 10 Jun 2024 10:23:09 +0300
Subject: [PATCH 043/280] Add a practice problem for graph/finding-cycle
---
src/graph/finding-cycle.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/graph/finding-cycle.md b/src/graph/finding-cycle.md
index d629d2feb..af0ebda9f 100644
--- a/src/graph/finding-cycle.md
+++ b/src/graph/finding-cycle.md
@@ -129,5 +129,6 @@ void find_cycle() {
```
### Practice problems:
+- [AtCoder : Reachability in Functional Graph](https://atcoder.jp/contests/abc357/tasks/abc357_e)
- [CSES : Round Trip](https://cses.fi/problemset/task/1669)
- [CSES : Round Trip II](https://cses.fi/problemset/task/1678/)
From eba7facf2b8606a76648713c3ca40eb90eb0e570 Mon Sep 17 00:00:00 2001
From: JJCUBER <34446698+JJCUBER@users.noreply.github.com>
Date: Mon, 10 Jun 2024 09:41:50 -0400
Subject: [PATCH 044/280] Refactor portion of Fenwick Tree
I tried to improve the grammar and use of notation/definitions in the first few sections.
---
src/data_structures/fenwick.md | 68 ++++++++++++++++++----------------
1 file changed, 37 insertions(+), 31 deletions(-)
diff --git a/src/data_structures/fenwick.md b/src/data_structures/fenwick.md
index 2ef0db409..2ccc5bb5e 100644
--- a/src/data_structures/fenwick.md
+++ b/src/data_structures/fenwick.md
@@ -6,44 +6,49 @@ e_maxx_link: fenwick_tree
# Fenwick Tree
-Let, $f$ be some group operation (binary associative function over a set with identity element and inverse elements) and $A$ be an array of integers of length $N$.
+Let $f$ be some group operation (a binary associative function over a set with an identity element and inverse elements) and $A$ be an array of integers of length $N$.
+Denote $f$'s infix notation as $\*$; that is, $f(x,y) = x\*y$ for arbitrary integers $x,y$.
+(Since this is associative, we will omit parentheses for order of application of $f$ when using infix notation.)
-Fenwick tree is a data structure which:
+The Fenwick tree is a data structure which:
-* calculates the value of function $f$ in the given range $[l, r]$ (i.e. $f(A_l, A_{l+1}, \dots, A_r)$) in $O(\log N)$ time;
-* updates the value of an element of $A$ in $O(\log N)$ time;
-* requires $O(N)$ memory, or in other words, exactly the same memory required for $A$;
-* is easy to use and code, especially, in the case of multidimensional arrays.
+* calculates the value of function $f$ in the given range $[l, r]$ $\left(\text{i.e. }A_l \* A_{l+1} \* \dots \* A_r)\right)$ in $O(\log N)$ time
+* updates the value of an element of $A$ in $O(\log N)$ time
+* requires $O(N)$ memory (the same amount required for $A$)
+* is easy to use and code, especially in the case of multidimensional arrays
-The most common application of Fenwick tree is _calculating the sum of a range_ (i.e. using addition over the set of integers $\mathbb{Z}$: $f(A_1, A_2, \dots, A_k) = A_1 + A_2 + \dots + A_k$).
+The most common application of a Fenwick tree is _calculating the sum of a range_.
+For example, using addition over the set of integers as the group operation, i.e. $f(x,y) = x + y$: the binary operation, $\*$, is $+$ in this case, so $A_l \* A_{l+1} \* \dots \* A_r = A_l + A_{l+1} + \dots + A_{r}$.
+(In terms of $f$, this would be $f(A_l, f(A_{l+1}, f(f(\dots, \dots),A_r)))$, or any other equivalent way to write this using associativity.)
-Fenwick tree is also called **Binary Indexed Tree**, or just **BIT** abbreviated.
-
-Fenwick tree was first described in a paper titled "A new data structure for cumulative frequency tables" (Peter M. Fenwick, 1994).
+The Fenwick tree is also called a **Binary Indexed Tree** (BIT).
+It was first described in a paper titled "A new data structure for cumulative frequency tables" (Peter M. Fenwick, 1994).
## Description
### Overview
-For the sake of simplicity, we will assume that function $f$ is just a *sum function*.
+For the sake of simplicity, we will assume that function $f$ is defined as $f(x,y) = x + y$ over the integers.
-Given an array of integers $A[0 \dots N-1]$.
-A Fenwick tree is just an array $T[0 \dots N-1]$, where each of its elements is equal to the sum of elements of $A$ in some range $[g(i), i]$:
+Suppose we are given an array of integers, $A[0 \dots N-1]$.
+(Note that we are using zero-based indexing.)
+A Fenwick tree is just an array, $T[0 \dots N-1]$, where each element is equal to the sum of elements of $A$ in some range, $[g(i), i]$:
-$$T_i = \sum_{j = g(i)}^{i}{A_j},$$
+$$T_i = \sum_{j = g(i)}^{i}{A_j}$$
where $g$ is some function that satisfies $0 \le g(i) \le i$.
-We will define the function in the next few paragraphs.
+We will define $g$ in the next few paragraphs.
-The data structure is called tree, because there is a nice representation of the data structure as tree, although we don't need to model an actual tree with nodes and edges.
-We will only need to maintain the array $T$ to handle all queries.
+The data structure is called a tree because there is a nice representation of it in the form of a tree, although we don't need to model an actual tree with nodes and edges.
+We only need to maintain the array $T$ to handle all queries.
**Note:** The Fenwick tree presented here uses zero-based indexing.
-Many people will actually use a version of the Fenwick tree that uses one-based indexing.
-Therefore you will also find an alternative implementation using one-based indexing in the implementation section.
+Many people use a version of the Fenwick tree that uses one-based indexing.
+As such, you will also find an alternative implementation which uses one-based indexing in the implementation section.
Both versions are equivalent in terms of time and memory complexity.
-Now we can write some pseudo-code for the two operations mentioned above - get the sum of elements of $A$ in the range $[0, r]$ and update (increase) some element $A_i$:
+Now we can write some pseudo-code for the two operations mentioned above.
+Below, we get the sum of elements of $A$ in the range $[0, r]$ and update (increase) some element $A_i$:
```python
def sum(int r):
@@ -60,20 +65,21 @@ def increase(int i, int delta):
The function `sum` works as follows:
-1. first, it adds the sum of the range $[g(r), r]$ (i.e. $T[r]$) to the `result`
-2. then, it "jumps" to the range $[g(g(r)-1), g(r)-1]$, and adds this range's sum to the `result`
-3. and so on, until it "jumps" from $[0, g(g( \dots g(r)-1 \dots -1)-1)]$ to $[g(-1), -1]$; that is where the `sum` function stops jumping.
+1. First, it adds the sum of the range $[g(r), r]$ (i.e. $T[r]$) to the `result`.
+2. Then, it "jumps" to the range $[g(g(r)-1), g(r)-1]$ and adds this range's sum to the `result`.
+3. This continues until it "jumps" from $[0, g(g( \dots g(r)-1 \dots -1)-1)]$ to $[g(-1), -1]$; this is where the `sum` function stops jumping.
-The function `increase` works with the same analogy, but "jumps" in the direction of increasing indices:
+The function `increase` works with the same analogy, but it "jumps" in the direction of increasing indices:
-1. sums of the ranges $[g(j), j]$ that satisfy the condition $g(j) \le i \le j$ are increased by `delta` , that is `t[j] += delta`. Therefore we updated all elements in $T$ that correspond to ranges in which $A_i$ lies.
+1. Sums of the range $[g(j), j]$ which satisfy the condition $g(j) \le i \le j$ are increased by `delta`; that is, `t[j] += delta`.
+Therefore, it updates all elements in $T$ that correspond to ranges in which $A_i$ lies.
-It is obvious that the complexity of both `sum` and `increase` depend on the function $g$.
-There are lots of ways to choose the function $g$, as long as $0 \le g(i) \le i$ for all $i$.
-For instance the function $g(i) = i$ works, which results just in $T = A$, and therefore summation queries are slow.
-We can also take the function $g(i) = 0$.
-This will correspond to prefix sum arrays, which means that finding the sum of the range $[0, i]$ will only take constant time, but updates are slow.
-The clever part of the Fenwick algorithm is, that there it uses a special definition of the function $g$ that can handle both operations in $O(\log N)$ time.
+The complexity of both `sum` and `increase` depend on the function $g$.
+There are many ways to choose the function $g$ such that $0 \le g(i) \le i$ for all $i$.
+For instance, the function $g(i) = i$ works, which yields $T = A$ (in which case, the summation queries are slow).
+We could also take the function $g(i) = 0$.
+This would correspond to prefix sum arrays (in which case, finding the sum of the range $[0, i]$ will only take constant time; however, updates are slow).
+The clever part of the algorithm for Fenwick trees is how it uses a special definition of the function $g$ which can handle both operations in $O(\log N)$ time.
### Definition of $g(i)$ { data-toc-label='Definition of ' }
From 52f8db223a9ba62533fdb9411a98ec45909bdc03 Mon Sep 17 00:00:00 2001
From: JJCUBER <34446698+JJCUBER@users.noreply.github.com>
Date: Mon, 10 Jun 2024 09:54:00 -0400
Subject: [PATCH 045/280] Unescape *'s
It seems that they render differently through github markdown vs the one used for the official site.
---
src/data_structures/fenwick.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/data_structures/fenwick.md b/src/data_structures/fenwick.md
index 2ccc5bb5e..c77ee4d62 100644
--- a/src/data_structures/fenwick.md
+++ b/src/data_structures/fenwick.md
@@ -7,18 +7,18 @@ e_maxx_link: fenwick_tree
# Fenwick Tree
Let $f$ be some group operation (a binary associative function over a set with an identity element and inverse elements) and $A$ be an array of integers of length $N$.
-Denote $f$'s infix notation as $\*$; that is, $f(x,y) = x\*y$ for arbitrary integers $x,y$.
+Denote $f$'s infix notation as $*$; that is, $f(x,y) = x*y$ for arbitrary integers $x,y$.
(Since this is associative, we will omit parentheses for order of application of $f$ when using infix notation.)
The Fenwick tree is a data structure which:
-* calculates the value of function $f$ in the given range $[l, r]$ $\left(\text{i.e. }A_l \* A_{l+1} \* \dots \* A_r)\right)$ in $O(\log N)$ time
+* calculates the value of function $f$ in the given range $[l, r]$ $\left(\text{i.e. }A_l * A_{l+1} * \dots * A_r)\right)$ in $O(\log N)$ time
* updates the value of an element of $A$ in $O(\log N)$ time
* requires $O(N)$ memory (the same amount required for $A$)
* is easy to use and code, especially in the case of multidimensional arrays
The most common application of a Fenwick tree is _calculating the sum of a range_.
-For example, using addition over the set of integers as the group operation, i.e. $f(x,y) = x + y$: the binary operation, $\*$, is $+$ in this case, so $A_l \* A_{l+1} \* \dots \* A_r = A_l + A_{l+1} + \dots + A_{r}$.
+For example, using addition over the set of integers as the group operation, i.e. $f(x,y) = x + y$: the binary operation, $*$, is $+$ in this case, so $A_l * A_{l+1} * \dots * A_r = A_l + A_{l+1} + \dots + A_{r}$.
(In terms of $f$, this would be $f(A_l, f(A_{l+1}, f(f(\dots, \dots),A_r)))$, or any other equivalent way to write this using associativity.)
The Fenwick tree is also called a **Binary Indexed Tree** (BIT).
From 9cf376aabf15b1bf33260c3de05c24c8c6c5e20e Mon Sep 17 00:00:00 2001
From: JJCUBER <34446698+JJCUBER@users.noreply.github.com>
Date: Mon, 10 Jun 2024 14:49:49 -0400
Subject: [PATCH 046/280] Remove extra parenthesis
---
src/data_structures/fenwick.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/data_structures/fenwick.md b/src/data_structures/fenwick.md
index c77ee4d62..ea54149bb 100644
--- a/src/data_structures/fenwick.md
+++ b/src/data_structures/fenwick.md
@@ -12,7 +12,7 @@ Denote $f$'s infix notation as $*$; that is, $f(x,y) = x*y$ for arbitrary intege
The Fenwick tree is a data structure which:
-* calculates the value of function $f$ in the given range $[l, r]$ $\left(\text{i.e. }A_l * A_{l+1} * \dots * A_r)\right)$ in $O(\log N)$ time
+* calculates the value of function $f$ in the given range $[l, r]$ (i.e. $A_l * A_{l+1} * \dots * A_r$) in $O(\log N)$ time
* updates the value of an element of $A$ in $O(\log N)$ time
* requires $O(N)$ memory (the same amount required for $A$)
* is easy to use and code, especially in the case of multidimensional arrays
From 50b011ab0a6e6974a4f848bdbf90042b8f6443bd Mon Sep 17 00:00:00 2001
From: Gabriel Ribeiro Paiva
Date: Mon, 10 Jun 2024 20:37:10 -0300
Subject: [PATCH 047/280] fix: fix format error in manhattan distance article
---
src/geometry/manhattan-distance.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md
index 7df382aee..eae318800 100644
--- a/src/geometry/manhattan-distance.md
+++ b/src/geometry/manhattan-distance.md
@@ -67,6 +67,7 @@ This means that if we keep the active set ordered by $x$ the candidates $s$ are
Now that we have the nearest point in the north-east direction, we rotate the points and repeat. It is possible to show that actually we also find this way the nearest point in the south-west direction, so we can repeat only 4 times, instead of 8.
In summary we:
+
- Sort the points by $x + y$ in non-decreasing order;
- For every point, we iterate over the active set starting with the point with the largest $x$ such that $x \leq x_p$, and we break the loop if $x_p - y_p \geq x_s - y_s$. For every valid point $s$ we add the edge $(s,p, dist(s,p))$ in our list;
- We add the point $p$ to the active set;
From ecbb9f10e3de37c12aba4b63b53ec34658eb987b Mon Sep 17 00:00:00 2001
From: Gabriel Ribeiro Paiva
Date: Mon, 10 Jun 2024 20:40:16 -0300
Subject: [PATCH 048/280] feat: add manhattan distance articles to navigation
---
src/navigation.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/navigation.md b/src/navigation.md
index de682c536..853cb57ad 100644
--- a/src/navigation.md
+++ b/src/navigation.md
@@ -142,6 +142,7 @@ search:
- [Delaunay triangulation and Voronoi diagram](geometry/delaunay.md)
- [Vertical decomposition](geometry/vertical_decomposition.md)
- [Half-plane intersection - S&I Algorithm in O(N log N)](geometry/halfplane-intersection.md)
+ - [Manhattan Distance](geometry/manhattan-distance.md)
- Graphs
- Graph traversal
- [Breadth First Search](graph/breadth-first-search.md)
From 5103c5644e917b89ded68cb66d51e72c5b64b9a4 Mon Sep 17 00:00:00 2001
From: NaimSS
Date: Wed, 12 Jun 2024 21:09:29 +0200
Subject: [PATCH 049/280] improve text and add images
---
src/geometry/manhattan-distance.md | 47 ++++++++++++++++++--
src/geometry/manhattan-mst-octants.png | Bin 0 -> 32513 bytes
src/geometry/manhattan-mst-sweep-line-1.png | Bin 0 -> 85072 bytes
src/geometry/manhattan-mst-sweep-line-2.png | Bin 0 -> 69362 bytes
src/geometry/manhattan-mst-uniqueness.png | Bin 0 -> 16233 bytes
5 files changed, 43 insertions(+), 4 deletions(-)
create mode 100644 src/geometry/manhattan-mst-octants.png
create mode 100644 src/geometry/manhattan-mst-sweep-line-1.png
create mode 100644 src/geometry/manhattan-mst-sweep-line-2.png
create mode 100644 src/geometry/manhattan-mst-uniqueness.png
diff --git a/src/geometry/manhattan-distance.md b/src/geometry/manhattan-distance.md
index eae318800..a448c3717 100644
--- a/src/geometry/manhattan-distance.md
+++ b/src/geometry/manhattan-distance.md
@@ -12,6 +12,8 @@ $d(p,q) = |p.x - q.x| + |p.y - q.y|$
This is informally know as the [Manhattan distance, or taxicab geometry](https://en.wikipedia.org/wiki/Taxicab_geometry), because we can think of the points as being intersections in a well designed city, like manhattan, where you can only move on the streets, as shown in the image below:
+
+
This images show some of the smallest paths from one black point to the other, all of them with distance $12$.
There are some interseting tricks and algorithms that can be done with this distance, and we will show some of them here.
@@ -31,7 +33,26 @@ Notice that we can extend this idea further for 2 (or more!) dimensions. For $d$
$max_{p, q \in P} (p.x + (-q.x)) + (p.y + (-q.y)) = max_{p \in P}(p.x + p.y) + max_{q \in P}(-q.x - q.y)$.
-As we made $p$ and $q$ independent, it is now easy to find the $p$ and $q$ that maximize the expression.
+As we made $p$ and $q$ independent, it is now easy to find the $p$ and $q$ that maximize the expression.
+
+The code below generalizes this to $d$ dimensions and runs in $O(n \cdot 2^d \cdot d)$.
+
+```cpp
+long long ans = 0;
+for(int msk=0;msk < (1< x - y$, and then do some casework. The images below give some intuition on why this is true.
+First, let's understand why it is enough to consider only the nearest neighbor in each octant. The idea is to show that for a point $s$ and any two other points $p$ and $q$ in the same octant, $dist(p, q) < max(dist(s, p), dist(s, q))$. This is important, because it shows that if there was a MST where $s$ is connected to both $p$ and $q$, we could erase one of these edges and add the edge $(p,q)$, which would decrease the total cost. To prove this, we assume without loss of generality that $p$ and $q$ are in the octanct $R_1$, which is defined by: $x_s \leq x$ and $x_s - y_s > x - y$, and then do some casework. The image below give some intuition on why this is true.
+
+
+*We can build some intuition that limitation of the octant make it impossible that $s$ is closer to both $p$ and $q$ then each other*
+
Therefore, the main question is how to find the nearest neighbor in each octant for every single of the $n$ points.
@@ -52,7 +80,14 @@ Therefore, the main question is how to find the nearest neighbor in each octant
For simplicity we focus on the north-east octant. All other directions can be found with the same algorithm by rotating the input.
-We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet, which we call "active set".
+We will use a sweep-line approach. We process the points from south-west to north-east, that is, by non-decreasing $x + y$. We also keep a set of points which don't have their nearest neighbor yet, which we call "active set". We add the images below to help visualize the algorithm.
+
+
+
+*In black with an arrow you can see the direction of the line-sweep. All the points below this lines are in the active set, and the points above are still not processed. In green we see the points which are in the octant of the processed point. In red the points that are not in the searched octant.*
+
+
+*In this image we see the active set after processing the point $p$. Note that the $2$ green points of the previous image had $p$ in its north-north-east octant and are not in the active set anymore, because they already found their nearest neighbor.*
When we add a new point point $p$, for every point $s$ that has it in it's octant we can safely assign $p$ as the nearest neighbor. This is true because their distance is $d(p,s) = |x_p - x_s| + |y_p - y_s| = (x_p + y_p) - (x_s + y_s)$, because $p$ is in the north-east octant. As all the next points will not have a smaller value of $x + y$ because of the sorting step, $p$ is guaranteed to have the smaller distance. We can then remove all such points from the active set, and finally add $p$ to the active set.
@@ -61,7 +96,9 @@ The next question is how to efficiently find which points $s$ have $p$ in the no
- $x_s \leq x_p$
- $x_p - y_p < x_s - y_s$
-Because no points in the active set are in the R_1 of another, we also have that for two points $q_1$ and $q_2$ in the active set, $x_{q_1} \neq x_{q_2}$ and $x_{q_1} < x_{q_2} \implies x_{q_1} - y_{q_1} \leq x_{q_2} - y_{q_2}$.
+Because no points in the active set are in the $R_1$ of another, we also have that for two points $q_1$ and $q_2$ in the active set, $x_{q_1} \neq x_{q_2}$ and $x_{q_1} < x_{q_2} \implies x_{q_1} - y_{q_1} \leq x_{q_2} - y_{q_2}$.
+
+You can try to visualize this on the images above by thinking of the ordering of $x - y$ as a "sweep-line" that goes from the north-west to the south-east, so perpendicular to the one that is drawn.
This means that if we keep the active set ordered by $x$ the candidates $s$ are consecutively placed. We can then find the largest $x_s \leq x_p$ and process the points in decreasing order of $x$ until the second condition $x_p - y_p < x_s - y_s$ breaks (we can actually allow that $x_p - y_p = x_s - y_s$ and that deals with the case of points with equal coordinates). Notice that because we remove from the set right after processing, this will have an amortized complexity of $O(n \log(n))$.
Now that we have the nearest point in the north-east direction, we rotate the points and repeat. It is possible to show that actually we also find this way the nearest point in the south-west direction, so we can repeat only 4 times, instead of 8.
@@ -77,6 +114,8 @@ In summary we:
Below you can find a implementation, based on the one from [KACTL](https://github.com/kth-competitive-programming/kactl/blob/main/content/geometry/ManhattanMST.h).
```{.cpp file=manhattan_mst.cpp}
+// Returns a list of edges in the format (weight, u, v).
+// Passing this list to Kruskal algorithm will give the Manhattan MST.
vector > manhattan_mst_edges(vector ps){
vector ids(ps.size());
iota(ids.begin(), ids.end(), 0);
diff --git a/src/geometry/manhattan-mst-octants.png b/src/geometry/manhattan-mst-octants.png
new file mode 100644
index 0000000000000000000000000000000000000000..29f3a8b638f13b6bf88eba06e8ff45e24e5cabe6
GIT binary patch
literal 32513
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
|M=tE?aoK*-`uE4m)yEZ-smOTT
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