restructure most

This commit is contained in:
Benjamin Qi 2020-06-16 13:09:59 -04:00
parent ada78ccd53
commit ad104c9eff
17 changed files with 853 additions and 380 deletions

View file

@ -123,6 +123,9 @@ do {
- Hint: For this problem, you can't do a full complete search; you have to do a reduced search)
- [Back and Forth](http://www.usaco.org/index.php?page=viewproblem2&cpid=857)
- Somewhat harder
- USACO Silver
- [Bovine Genomics](http://usaco.org/index.php?page=viewproblem2&cpid=739)
- [Field Reduction](http://usaco.org/index.php?page=viewproblem2&cpid=642)
- CSES
- (Permutations) [Chessboard and Queens](https://cses.fi/problemset/task/1624)

View file

@ -0,0 +1,41 @@
---
id: intro-graphs
title: Introduction to Graphs
author: Darren Yao, Benjamin Qi
---
**Graph theory** is one of the most important topics at the Silver level and above, although some basic problems occasionally appear in Bronze.
<!-- END DESCRIPTION -->
Graphs can be used to represent many things, from images to wireless signals, but one of the simplest analogies is to a map. Consider a map with several cities and highways connecting the cities. Some of the problems relating to graphs (which will be covered in Silver) are:
- If we have a map with some cities and roads, what's the shortest distance I have to travel to get from point A to point B?
- Consider a map of cities and roads. Is city A connected to city B? Consider a region to be a group of cities such that each city in the group can reach any other city in said group, but no other cities. How many regions are in this map, and which cities are in which region?
For now, it suffices to learn how graphs are represented.
## Tutorial
- Recommended
- CPH 11
- Intro to USACO 10.1 - 10.3
- [CSAcademy Graph Intro](https://csacademy.com/lesson/introduction_to_graphs)
- [CSAcademy Graph Representations](https://csacademy.com/lesson/graph_representation)
- Usually, adjacency lists are used.
- Other
- [Topcoder Graphs Pt 1](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-1/)
## Representing Trees?
## USACO Bronze Problems
(add some more)
- Tree
- [Factory](http://usaco.org/index.php?page=viewproblem2&cpid=940)
- [Family Tree](http://usaco.org/index.php?page=viewproblem2&cpid=833)
- [Grass Planting (Silver)](http://usaco.org/index.php?page=viewproblem2&cpid=894)
- General Graph
- [The Great Revegetation](http://usaco.org/index.php?page=viewproblem2&cpid=916)

View file

@ -31,8 +31,6 @@ using namespace std;
int main() {
pair<string, int> myPair = make_pair("Testing", 123);
cout << myPair.first << " " << myPair.second << endl; // Testing 123
vector<pair<int,int>> v = {{2,4},{1,3},{3,4},{3,1}};
sort(begin(v),end(v)); // {(1, 3), (2, 4), (3, 1), (3, 4)}
}
/* Output

View file

@ -133,17 +133,18 @@ int main() {
### Problems
- USACO
- [USACO Silver - Cow Dance Show](http://www.usaco.org/index.php?page=viewproblem2&cpid=690)
- USACO Silver
- [Moo Buzz](http://www.usaco.org/index.php?page=viewproblem2&cpid=966)
- [Cow Dance Show](http://www.usaco.org/index.php?page=viewproblem2&cpid=690)
- binary search on $K$ and simulate
- [USACO Silver - Convention](http://www.usaco.org/index.php?page=viewproblem2&cpid=858)
- [Convention](http://www.usaco.org/index.php?page=viewproblem2&cpid=858)
- determine whether $M$ buses suffice if every cow waits at most $T$ minutes
- use a greedy strategy (applies to next two problems as well)
- [USACO Silver - Angry Cows](http://usaco.org/index.php?page=viewproblem2&cpid=594)
- [Angry Cows](http://usaco.org/index.php?page=viewproblem2&cpid=594)
- check in $O(N)$ how many haybales you can destroy with fixed radius $R$
- [USACO Silver - Social Distancing](http://www.usaco.org/index.php?page=viewproblem2&cpid=1038)
- [Social Distancing](http://www.usaco.org/index.php?page=viewproblem2&cpid=1038)
- check in $O(N+M)$ how many cows you can place with distance $D$
- [USACO Silver - Loan Repayment](http://www.usaco.org/index.php?page=viewproblem2&cpid=991)
- [Loan Repayment](http://www.usaco.org/index.php?page=viewproblem2&cpid=991)
- requires some rather tricky analysis to speed up naive $O(N\log N)$ solution
- CF
- [The Meeting Place Cannot Be Changed](http://codeforces.com/contest/782/problem/B) [](48)

113
content/4_Silver/Cyc.md Normal file
View file

@ -0,0 +1,113 @@
---
id: cyc
title: Cycle Detection & Functional Graphs
author: Siyong Huang
prerequisites:
-
- Silver - Depth First Search
---
A *cycle* is a non-empty path of distinct edges that start and end at the same node.
<!-- END DESCRIPTION -->
*Cycle detection* determines properties of cycles in a directed or undirected graph, such as whether each node of the graph is part of a cycle or just checking whether a cycle exists.
A related topic is **strongly connected components**, a platinum level concept.
### Functional Graphs
Links:
- CPH 16.3: successor paths
- CPH 16.4: cycle detection in successor graph
In silver-level directed cycle problems, it is generally the case that each node has exactly one edge going out of it. This is known as a **successor graph** or a **functional graph.**
The following sample code counts the number of cycles in such a graph. The "stack" contains nodes that can reach the current node. If the current node points to a node `v` on the stack (`on_stack[v]` is true), then we know that a cycle has been created. However, if the current node points to a node `v` that has been previously visited but is not on the stack, then we know that the current chain of nodes points into a cycle that has already been considered.
```cpp
//UNTESTED
//Each node points to next_node[node]
bool visited[MAXN], on_stack[MAXN];
int number_of_cycles = 0, next_node[MAXN];
void dfs(int n)
{
visited[n] = on_stack[n] = true;
int u = next_node[n];
if(on_stack[u])
number_of_cycles++;
else if(!visited[u])
dfs(u);
on_stack[n] = false;
}
int main()
{
//read input, etc
for(int i = 1;i <= N;i++)
if(!visited[i])
dfs(i);
}
```
The same general idea is implemented below to find any cycle in a directed graph (if one exists).
```cpp
//UNTESTED
bool visited[MAXN], on_stack[MAXN];
vector<int> adj[MAXN];
vector<int> cycle;
bool dfs(int n)
{
visited[n] = on_stack[n] = true;
for(int u:adj[n])
{
if(on_stack[u])
return cycle.push_back(v), cycle.push_back(u), on_stack[n] = on_stack[u] = false, true;
else if(!visited[u])
{
if(dfs(u))
if(on_stack[n])
return cycle.push_back(n), on_stack[n] = false, true;
else
return false;
if(!cycle.empty())
return false;
}
}
on_stack[n] = false;
return false;
}
int main()
{
//take input, etc
for(int i = 1;cycle.empty() && i <= N;i++)
dfs(i);
if(cycle.empty())
printf("No cycle found!\n");
else
{
reverse(cycle.begin(), cycle.end());
printf("Cycle of length %u found!\n", cycle.size());
for(int n : cycle) printf("%d ", n);
printf("\n");
}
}
```
### Problems
- [Codeforces 1020B. Badge (Very Easy)](https://codeforces.com/contest/1020/problem/B)
- Try to solve the problem in O(N)!
- [The Bovine Shuffle (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=764)
- [Swapity Swapity Swap (Very Hard)](http://www.usaco.org/index.php?page=viewproblem2&cpid=1014)
- [CSES Round Trip (undirected cycle)](https://cses.fi/problemset/task/1669)
- [CSES Round Trip II (directed cycle)](https://cses.fi/problemset/task/1678)
- POI
- [Mafia](https://szkopul.edu.pl/problemset/problem/w3YAoAT3ej27YeiaNWjK57_G/site/?key=statement)
- [Spies](https://szkopul.edu.pl/problemset/problem/r6tMTfvQFPAEfQioYMCQndQe/site/?key=statement)
- [Frog](https://szkopul.edu.pl/problemset/problem/qDH9CkBHZKHY4vbKRBlXPrA7/site/?key=statement)

View file

@ -2,28 +2,18 @@
id: dfs
title: Depth First Search
author: Siyong Huang
prerequisites:
-
- Bronze - Introduction to Graphs
---
- Introduction to Graphs
- Depth First Search (DFS)
- Flood Fill
- Graph Two-Coloring
- Cycle Detection
<!-- END DESCRIPTION -->
## Introduction to Graphs
- Recommended
- CPH 11
- [CSAcademy Graph Intro](https://csacademy.com/lesson/introduction_to_graphs)
- [CSAcademy Graph Representations](https://csacademy.com/lesson/graph_representation)
- Usually, adjacency lists are used.
- Additional
- [Topcoder Graphs Pt 1](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-1/)
- [Topcoder Graphs Pt 2](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-2/)
## Depth First Search (DFS)
*Depth First Search*, more commonly DFS, is a fundamental graph algorithm that traverses an entire connected component. The rest of this document describes various applications of DFS. Of course, it is one possible way to implement flood fill. *Breadth first search* (BFS) is **not** required for silver.
@ -36,9 +26,10 @@ author: Siyong Huang
- CPH 12.1
- [CSAcademy DFS](https://csacademy.com/lesson/depth_first_search/)
- Additional:
- [CPC.7](https://github.com/SuprDewd/T-414-AFLV/tree/master/07_graphs_1)
- [cp-algo DFS](https://cp-algorithms.com/graph/depth-first-search.html)
- hard to parse if this is your first time learning about DFS
- [CPC.7](https://github.com/SuprDewd/T-414-AFLV/tree/master/07_graphs_1)
- [Topcoder Graphs Pt 2](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-2/)
### Problems
@ -54,11 +45,16 @@ author: Siyong Huang
- [Moocast, Silver (Easy)](http://usaco.org/index.php?page=viewproblem2&cpid=668)
- [Pails (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=620)
- [Milk Visits (Normal)](http://www.usaco.org/index.php?page=viewproblem2&cpid=968)
- [Count Cross](http://usaco.org/index.php?page=viewproblem2&cpid=716)
- [Wormhole Sort (Normal)](http://www.usaco.org/index.php?page=viewproblem2&cpid=992)
- also binary search on the answer
- [Fence Planning](http://usaco.org/index.php?page=viewproblem2&cpid=944)
- [Moo Particle](http://www.usaco.org/index.php?page=viewproblem2&cpid=1040)
- Other
- [POI Hotels](https://szkopul.edu.pl/problemset/problem/gDw3iFkeVm7ZA3j_16-XR7jI/site/?key=statement) [](61)
- [Kattis Birthday Party (Easy)](https://open.kattis.com/problems/birthday)
- DFS with each edge removed
## Flood Fill
@ -77,12 +73,16 @@ author: Siyong Huang
- [Ice Perimeter (Easy)](http://usaco.org/index.php?page=viewproblem2&cpid=895)
- [Switching on the Lights (Normal)](http://www.usaco.org/index.php?page=viewproblem2&cpid=570)
- [Build Gates (Normal)](http://www.usaco.org/index.php?page=viewproblem2&cpid=596)
- [Milk Pails (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=620)
- [Where's Bessie?](http://usaco.org/index.php?page=viewproblem2&cpid=740)
- [Why Did the Cow Cross the Road III, Silver (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=716)
- [Multiplayer Moo (Hard)](http://usaco.org/index.php?page=viewproblem2&cpid=836)
- [Snow Boots (Hard)](http://usaco.org/index.php?page=viewproblem2&cpid=811)
- [Mooyo Mooyo](http://usaco.org/index.php?page=viewproblem2&cpid=860)
## Graph Two-Coloring
*Graph two-colorings* is assigning a boolean value to each node of the graph, dictated by the edge configuration
*Graph two-coloring* refers to assigning a boolean value to each node of the graph, dictated by the edge configuration
The most common example of a two-colored graph is a *bipartite graph*, in which each edge connects two nodes of opposite colors.
- [CSES Building Teams](https://cses.fi/problemset/task/1668)
@ -119,107 +119,4 @@ void dfs(int node)
### Problems
- [CF Bipartiteness](http://codeforces.com/contest/862/problem/B) [](49)
- [The Great Revegetation (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=920)
## Cycle Detection
A *cycle* is a non-empty path of distinct edges that start and end at the same node. *Cycle detection* determines properties of cycles in a directed or undirected graph, such as whether each node of the graph is part of a cycle or just checking whether a cycle exists.
A related topic is **strongly connected components**, a platinum level concept.
### Functional Graphs
Links:
- CPH 16.3: successor paths
- CPH 16.4: cycle detection in successor graph
In silver-level directed cycle problems, it is generally the case that each node has exactly one edge going out of it. This is known as a **successor graph** or a **functional graph.**
The following sample code counts the number of cycles in such a graph. The "stack" contains nodes that can reach the current node. If the current node points to a node `v` on the stack (`on_stack[v]` is true), then we know that a cycle has been created. However, if the current node points to a node `v` that has been previously visited but is not on the stack, then we know that the current chain of nodes points into a cycle that has already been considered.
```cpp
//UNTESTED
//Each node points to next_node[node]
bool visited[MAXN], on_stack[MAXN];
int number_of_cycles = 0, next_node[MAXN];
void dfs(int n)
{
visited[n] = on_stack[n] = true;
int u = next_node[n];
if(on_stack[u])
number_of_cycles++;
else if(!visited[u])
dfs(u);
on_stack[n] = false;
}
int main()
{
//read input, etc
for(int i = 1;i <= N;i++)
if(!visited[i])
dfs(i);
}
```
The same general idea is implemented below to find any cycle in a directed graph (if one exists).
```cpp
//UNTESTED
bool visited[MAXN], on_stack[MAXN];
vector<int> adj[MAXN];
vector<int> cycle;
bool dfs(int n)
{
visited[n] = on_stack[n] = true;
for(int u:adj[n])
{
if(on_stack[u])
return cycle.push_back(v), cycle.push_back(u), on_stack[n] = on_stack[u] = false, true;
else if(!visited[u])
{
if(dfs(u))
if(on_stack[n])
return cycle.push_back(n), on_stack[n] = false, true;
else
return false;
if(!cycle.empty())
return false;
}
}
on_stack[n] = false;
return false;
}
int main()
{
//take input, etc
for(int i = 1;cycle.empty() && i <= N;i++)
dfs(i);
if(cycle.empty())
printf("No cycle found!\n");
else
{
reverse(cycle.begin(), cycle.end());
printf("Cycle of length %u found!\n", cycle.size());
for(int n : cycle) printf("%d ", n);
printf("\n");
}
}
```
### Problems
- [Codeforces 1020B. Badge (Very Easy)](https://codeforces.com/contest/1020/problem/B)
- Try to solve the problem in O(N)!
- [The Bovine Shuffle (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=764)
- [Swapity Swapity Swap (Very Hard)](http://www.usaco.org/index.php?page=viewproblem2&cpid=1014)
- [CSES Round Trip (undirected cycle)](https://cses.fi/problemset/task/1669)
- [CSES Round Trip II (directed cycle)](https://cses.fi/problemset/task/1678)
- POI
- [Mafia](https://szkopul.edu.pl/problemset/problem/w3YAoAT3ej27YeiaNWjK57_G/site/?key=statement)
- [Spies](https://szkopul.edu.pl/problemset/problem/r6tMTfvQFPAEfQioYMCQndQe/site/?key=statement)
- [Frog](https://szkopul.edu.pl/problemset/problem/qDH9CkBHZKHY4vbKRBlXPrA7/site/?key=statement)
- [The Great Revegetation (Normal)](http://usaco.org/index.php?page=viewproblem2&cpid=920)

View file

@ -9,4 +9,17 @@ prerequisites:
More practice with data structures introduced in bronze.
<!-- END DESCRIPTION -->
<!-- END DESCRIPTION -->
(set iterators?)
## USACO Problems
- Silver
- [Cities & States](http://usaco.org/index.php?page=viewproblem2&cpid=667)
- [Milk Measurement](http://usaco.org/index.php?page=viewproblem2&cpid=763)
- [Convention II](http://usaco.org/index.php?page=viewproblem2&cpid=859)
- Gold
- [Snow Boots](http://www.usaco.org/index.php?page=viewproblem2&cpid=813)
- [Springboards](http://www.usaco.org/index.php?page=viewproblem2&cpid=995)
- hard?

View file

@ -2,35 +2,176 @@
id: greedy
title: "Greedy Algorithms"
author: Darren Yao
prerequisites:
-
- Silver - Sorting with Custom Comparators
---
**Greedy** algorithms select the optimal choice at each step instead of looking at the solution space as a whole. This reduces the problem to a smaller problem at each step.
<!-- END DESCRIPTION -->
Greedy does not refer to a single algorithm, but rather a way of thinking that is applied to problems. There's no one way to do greedy algorithms. Hence, we use a selection of well-known examples to help you understand the greedy paradigm.
Greedy does not refer to a single algorithm, but rather a way of thinking that is applied to problems. There's no one way to do greedy algorithms. Hence, we use a selection of well-known examples to help you understand the greedy paradigm.
Usually, when using a greedy algorithm, there is a heuristic or value function that determines which choice is considered most optimal.
Usually, when using a greedy algorithm, there is a heuristic or value function that determines which choice is considered most optimal. Usually (but not always) some sorting step is involved.
(convert Intro to USACO Chapter 9, Interval Cover)
# Tutorials
## Additional Resources
- CPH 6
- [CPC.5](https://github.com/SuprDewd/T-414-AFLV/tree/master/05_greedy_algorithms)
## Example: Studying Algorithms
### Statement
Steph wants to improve her knowledge of algorithms over winter break. She has a total of $X$ ($1 \leq X \leq 10^4$) minutes to dedicate to learning algorithms. There are $N$ ($1 \leq N \leq 100$) algorithms, and each one of them requires $a_i$ ($1 \leq a_i \leq 100$) minutes to learn. Find the maximum number of algorithms she can learn.
### Solution
The first observation we make is that Steph should prioritize learning algorithms from easiest to hardest; in other words, start with learning the algorithm that requires the least amount of time, and then choose further algorithms in increasing order of time required. Let's look at the following example:
$$X = 15, \qquad N = 6, \qquad a_i = \{ 4, 3, 8, 4, 7, 3 \}$$
After sorting the array, we have $\{ 3, 3, 4, 4, 7, 8 \}$. Within the maximum of 15 minutes, Steph can learn four algorithms in a total of $3+3+4+4 = 14$ minutes.
The implementation of this algorithm is very simple. We sort the array, and then take as many elements as possible while the sum of times of algorithms chosen so far is less than $X$. Sorting the array takes $O(N \log N)$ time, and iterating through the array takes $O(N)$ time, for a total time complexity of $O(N \log N)$.
```java
// read in the input, store the algorithms in int[] algorithms
Arrays.sort(algorithms);
int count = 0; // number of minutes used so far
int i = 0;
while(count + algorithms[i] <= x){
// while there is enough time, learn more algorithms
count += algorithms[i];
i++;
}
pw.println(i); // print the ans
pw.close();
```
## The Scheduling Problem
There are $N$ events, each described by their starting and ending times. Jason would like to attend as many events as possible, but he can only attend one event at a time, and if he chooses to attend an event, he must attend the entire event. Traveling between events is instantaneous.
### Bad Greedy: Earliest Starting Next Event
One possible ordering for a greedy algorithm would always select the next possible event that begins as soon as possible. Let's look at the following example, where the selected events are highlighted in red:
<!-- \begin{center}
\begin{tikzpicture}[ultra thick]
\draw[red](1, 2.5) -- (4, 2.5);
\draw(2, 2) -- (5, 2);
\draw[red](5, 1.5) -- (7, 1.5);
\draw(6, 1) -- (7, 1);
\end{tikzpicture}
\end{center} -->
In this example, the greedy algorithm selects two events, which is optimal. However, this doesn't always work, as shown by the following counterexample:
<!-- \begin{center}
\begin{tikzpicture}[ultra thick]
\draw[red](1, 2.5) -- (10, 2.5);
\draw(2, 2) -- (5, 2);
\draw(6, 1.5) -- (7, 1.5);
\draw(8, 1) -- (11, 1);
\end{tikzpicture}
\end{center} -->
In this case, the greedy algorithm selects to attend only one event. However, the optimal solution would be the following:
<!-- \begin{center}
\begin{tikzpicture}[ultra thick]
\draw(1, 2.5) -- (10, 2.5);
\draw[red](2, 2) -- (5, 2);
\draw[red](6, 1.5) -- (7, 1.5);
\draw[red](8, 1) -- (11, 1);
\end{tikzpicture}
\end{center} -->
### Correct Greedy: Earliest Ending Next Event
Instead, we can select the event that ends as early as possible. This correctly selects the three events.
<!-- \begin{center}
\begin{tikzpicture}[ultra thick]
\draw(1, 2.5) -- (10, 2.5);
\draw[red](2, 2) -- (5, 2);
\draw[red](6, 1.5) -- (7, 1.5);
\draw[red](8, 1) -- (11, 1);
\end{tikzpicture}
\end{center} -->
In fact, this algorithm always works. A brief explanation of correctness is as follows. If we have two events $E_1$ and $E_2$, with $E_2$ ending later than $E_1$, then it is always optimal to select $E_1$. This is because selecting $E_1$ gives us more choices for future events. If we can select an event to go after $E_2$, then that event can also go after $E_1$, because $E_1$ ends first. Thus, the set of events that can go after $E_2$ is a subset of the events that can go after $E_1$, making $E_1$ the optimal choice.
For the following code, let's say we have the array `events` of events, which each contain a start and an end point. We'll be using the following static class to store each event (a review of the previous chapter!)
```java
static class Event implements Comparable<Event>{
int start; int end;
public Event(int s, int e){
start = s; end = e;
}
public int compareTo(Event e){
return Integer.compare(this.end, e.end);
}
}
```
```java
// read in the input, store the events in Event[] events.
Arrays.sort(events); // sorts by comparator we defined above
int currentEventEnd = -1; // end of event currently attending
int ans = 0; // how many events were attended?
for(int i = 0; i < n; i++){ // process events in order of end time
if(events[i].start >= currentEventEnd){ // if event can be attended
// we know that this is the earliest ending event that we can attend
// because of how the events are sorted
currentEventEnd = events[i].end;
ans++;
}
}
pw.println(ans);
pw.close();
```
## When Greedy Fails
We'll provide a few common examples of when greedy fails, so that you can avoid falling into obvious traps and wasting time getting wrong answers in contest.
### Coin Change
This problem gives several coin denominations, and asks for the minimum number of coins needed to make a certain value. Greedy algorithms can be used to solve this problem only in very specific cases (it can be proven that it works for the American as well as the Euro coin systems). However, it doesn't work in the general case. For example, let the coin denominations be $\{1, 3, 4\}$, and say the value we want is 6. The optimal solution is $\{3, 3\}$, which requires only two coins, but the greedy method of taking the highest possible valued coin that fits in the remaining denomination gives the solution $\{4, 1, 1\}$, which is incorrect.
### Knapsack
The knapsack problem gives a number of items, each having a weight and a value, and we want to choose a subset of these items. We are limited to a certain weight, and we want to maximize the value of the items that we take.
Let's take the following example, where we have a maximum capacity of 4:
<!-- \begin{center}
\begin{tabular}{c c c c}
\toprule
Item & Weight & Value & Value Per Weight \\
\midrule
A & 3 & 18 & 6 \\
B & 2 & 10 & 5 \\
C & 2 & 10 & 5 \\
\bottomrule
\end{tabular}
\end{center} -->
If we use greedy based on highest value first, we choose item A and then we are done, as we don't have remaining weight to fit either of the other two. Using greedy based on value per weight again selects item A and then quits. However, the optimal solution is to select items B and C, as they combined have a higher value than item A alone. In fact, there is no working greedy solution. The solution to this problem uses **dynamic programming**, which is covered in gold.
# Problems
USACO
- [USACO Why Did the Cow Cross the Road](http://www.usaco.org/index.php?page=viewproblem2&cpid=714)
- [USACO Rest Stops](http://www.usaco.org/index.php?page=viewproblem2&cpid=810)
- [USACO High Card Wins](http://usaco.org/index.php?page=viewproblem2&cpid=571)
Misc
- [Sure Bet](https://csacademy.com/contest/archive/task/sure-bet/)
- [Did you Mean...](http://codeforces.com/contest/860/problem/A)
- [Permutation](http://codeforces.com/problemset/problem/864/D)
- [Bus](http://codeforces.com/problemset/problem/864/C)
- [Kayaking](http://codeforces.com/problemset/problem/863/B)
- USACO Silver
- [Lemonade Line](http://usaco.org/index.php?page=viewproblem2&cpid=835)
- [Why Did the Cow Cross the Road](http://www.usaco.org/index.php?page=viewproblem2&cpid=714)
- first step: sort!
- [Berry Picking](http://www.usaco.org/index.php?page=viewproblem2&cpid=990)
- [Rest Stops](http://www.usaco.org/index.php?page=viewproblem2&cpid=810)
- [High Card Wins](http://usaco.org/index.php?page=viewproblem2&cpid=571)
- Misc
- [Sure Bet](https://csacademy.com/contest/archive/task/sure-bet/)
- [Did you Mean...](http://codeforces.com/contest/860/problem/A)
- [Permutation](http://codeforces.com/problemset/problem/864/D)
- [Bus](http://codeforces.com/problemset/problem/864/C)
- [Kayaking](http://codeforces.com/problemset/problem/863/B)

View file

@ -10,6 +10,10 @@ Introduces sorting, binary search, coordinate compression.
**Sorting** is exactly what it sounds like: arranging items in some particular order.
## Additional Resources
- CPH 3 (once again, very good)
## Sorting Algorithms
(why are these important?)
@ -18,6 +22,7 @@ There are many sorting algorithms, here are some sources to learn about the popu
- [Bubble Sort](https://www.hackerrank.com/challenges/ctci-bubble-sort/problem)
- [Out of Sorts (Silver)](http://www.usaco.org/index.php?page=viewproblem2&cpid=834)
- hard!
- [Quicksort](https://www.hackerearth.com/practice/algorithms/sorting/quick-sort/tutorial/)
- [Mergesort](https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/tutorial/)
@ -79,13 +84,4 @@ However, each of the points are in the range $0 \ldots 1,000,000,000$, meaning y
Now, we can map distinct points to smaller integers without gaps. For example, if the haybales existed at positions $[1, 4, 5, 9]$ and queries were $(1, 2)$ and $(4, 6)$, we can place the integers together and map them from $[1, 2, 4, 5, 6, 9] \rightarrow [1, 2, 3, 4, 5, 6]$. This effectively transforms the haybale positions into $[1, 3, 4, 6]$ and the queries into $1, 2$ and $3, 5$.
By compressing queries and haybale positions, we've transformed the range of points to $0 \ldots N + 2Q$, allowing us to store prefix sums to effectively query for the number of haybales in a range.
## Problems and Other Tutorials
- CPH 3 (once again, very good)
- [Mooyo Mooyo](http://www.usaco.org/index.php?page=viewproblem2&cpid=860)
- Not a sorting problem, but you can use sorting to simulate gravity nicely.
- Write a custom comparator (read below) which puts zeroes at the front and use stable_sort to keep the relative order of other elements the same.
- [Meetings (Hard!)](http://www.usaco.org/index.php?page=viewproblem2&cpid=967)
By compressing queries and haybale positions, we've transformed the range of points to $0 \ldots N + 2Q$, allowing us to store prefix sums to effectively query for the number of haybales in a range.

View file

@ -73,18 +73,22 @@ This technique is also known as *cumulative sum* or *partial sums*.
## USACO Silver Problems
These problems are relatively straightforward.
Relatively straightforward.
- [USACO Breed Counting](http://www.usaco.org/index.php?page=viewproblem2&cpid=572)
- [USACO Subsequences Summing to Seven](http://www.usaco.org/index.php?page=viewproblem2&cpid=595)
- [Breed Counting](http://www.usaco.org/index.php?page=viewproblem2&cpid=572)
- [Subsequences Summing to Seven](http://www.usaco.org/index.php?page=viewproblem2&cpid=595)
- [Hoof Paper Scissors](http://usaco.org/index.php?page=viewproblem2&cpid=691)
- [Max Cross](http://usaco.org/index.php?page=viewproblem2&cpid=715)
- [Homework](http://usaco.org/index.php?page=viewproblem2&cpid=762)
Now we'll look at some extensions.
## Max Subarray Sum
- [Maximum Subarray Sum](https://cses.fi/problemset/task/1643)
- [CSES](https://cses.fi/problemset/task/1643)
(Note: This problem has a solution known as Kadane's Algorithm. Please *don't* use that solution; try to solve it with prefix sums.)
<details>
<summary>Why are the two methods equivalent?</summary>
Consider the desired maximum subarray. As you go along from left to right, the prefix sum solution will mark the start of that subarray as the "current minimum prefix sum". Kadane's Algorithm, on the other hand, will set the current value to 0 at that point. As both solutions iterate through the array, they eventually find the right side of the maximum sum, and they find the answer to the problem at that location. In essence, Kadane's Algorithm stores the maximum sum of a subarray that ends at the current location (which is what the prefix sum solution calculates on each iteration), but it calculates this value greedily instead.

View file

@ -1,197 +1,68 @@
---
id: sorting-custom
title: "Sorting with Custom Comparators"
author: Siyong Huang, Michael Cao
author: Darren Yao
prerequisites:
-
- Silver - Introduction to Sorting
---
*Custom comparators* define how the elements are ordered.
Both Java and C++ have built-in functions for sorting. However, if we use custom objects, or if we want to sort elements in a different order, then we'll need to use a **custom comparator**.
<!-- END DESCRIPTION -->
Let's consider a graph with weighted edges (pairs of values), such as in the problem [Wormhole Sort (Silver)](http://www.usaco.org/index.php?page=viewproblem2&cpid=992) and we wanted to sort the edges in nondecreasing order of weight, as we do in this problem (read editorial for more info). If we directly stored the edge weights and sorted them, we would have a sorted list of edge weights, but it would be impossible to tell which weights corresponded to which edges. However, if we create a class representing the edges and define a custom comparator to sort them by weight, we can sort the edges in ascending order while also keeping track of their endpoints.
## Comparators
Below are instructions to write such a custom comparator. This section is very language-specific and will be separated by language.
Normally, sorting functions rely on moving objects with a lower value in front of objects with a higher value if sorting in ascending order, and vice versa if in descending order. This is done through comparing two objects at a time.
### C++
Side note: In C++, a comparator must return false for two identical objects (not doing so results in undefined behavior and potentially RTE)
What a comparator does is compare two objects as follows, based on our comparison criteria:
#### Comparators for Sorting
- If object $x$ is less than object $y$, return `true`
- If object $x$ is greater than or equal to object $y$, return `false`
Essentially, the comparator determines whether object $x$ belongs to the left of object $y$ in a sorted ordering. A comparator **must** return false for two identical objects (not doing so results in undefined behavior and potentially RTE).
In addition to returning the correct answer, comparators should also satisfy the following conditions:
There are 2 main ways to have a custom comparator in c++
- The function must be consistent with respect to reversing the order of the arguments: if $x \neq y$ and `compare(x, y)}`is positive, then `compare(y, x)` should be negative and vice versa
- The function must be transitive. If `compare(x, y)` is true and `compare(y, z)` is true, then `compare(x, z)}` should also be true. If the first two compare functions both return `false`, the third must also return `false`.
Type 1) Overloading operator
- Pro:
- This is the easiest to implement
- Easy to work with STL
- Con:
- Only works for objects (not primitives)
- Only supports two types of comparisons (less than (<) and greater than (>))
A generic way of implementing a custom comparator is to define a function. For our example, we'll use a `struct` of a Person that contains a person's height and weight, and sort in ascending order by height. A `struct` is essentially a user-defined data structure:
<!-- Tested -->
```cpp
struct Foo
{
int Bar;
Foo(int _Bar=-1):Bar(_Bar){}
bool operator < (const Foo& foo2) const {return Bar < foo2.Bar;}
struct Person {
int height;
int weight;
};
const int N = 8;
int main()
{
Foo a[N];
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
int main() {
Person p;
p.height = 60; // assigns 60 to the height of p
p.weight = 100; // assigns 100 to the weight of p
}
```
Output:
```
(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0)
(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18)
```
Type 2) Function
- Pro:
- Works for both objects and primitives
- Supports many different comparators for the same object
- Con:
- More difficult to implement
- Extra care needs to be taken to support STL
Let's say we have an array `Person arr[N]`. To sort the array, we need to make custom comparator which will be a function, and then pass the function as a parameter into the build-in sort function:
<!-- Tested -->
```cpp
struct Foo
{
int Bar;
Foo(int _Bar=-1):Bar(_Bar){}
};
const int N = 8;
Foo a[N];
bool cmp1(Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;}
auto cmp2 = [](Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;};//requires c++11 or above
int main()
{
printf("--- Method 1 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, [](Foo foo1, Foo foo2){return foo1.Bar<foo2.Bar;});//requires c++11 or above
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
bool cmp(Person a, Person b) {
return a.height < b.height;
}
printf("--- Method 2 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, cmp1);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
printf("--- Method 3 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, cmp2);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
int main() {
sort(arr, arr+N, cmp); // sorts the array in ascending order by height
}
```
Output:
```
--- Method 1 ---
(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0)
(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18)
--- Method 2 ---
(Foo: 5) (Foo: 13) (Foo: 18) (Foo: 0) (Foo: 9) (Foo: 4) (Foo: 2) (Foo: 15)
(Foo: 0) (Foo: 2) (Foo: 4) (Foo: 5) (Foo: 9) (Foo: 13) (Foo: 15) (Foo: 18)
--- Method 3 ---
(Foo: 1) (Foo: 1) (Foo: 18) (Foo: 0) (Foo: 11) (Foo: 12) (Foo: 1) (Foo: 5)
(Foo: 0) (Foo: 1) (Foo: 1) (Foo: 1) (Foo: 5) (Foo: 11) (Foo: 12) (Foo: 18)
```
#### Comparators for STL
Operator overloading works as expected for using in STL. If you are sorting elements in reverse order, you can use the STL `greater` comparator ([click for documentation](https://en.cppreference.com/w/cpp/utility/functional/greater)) instead.
For function comparators, some extra care needs to be taken:
<!-- Reasonably Tested -->
```cpp
struct foo
{
//members
};
auto cmp1 = [](const foo& a, const foo& b){return /*comparator function*/;};
//This way is shorter to write, but don't forget to pass the comparator as a parameter in the constructor!
//If you need to create an array of the objects, I would either use pointers of the method shown below
set<foo, decltype(cmp1)> Set1(cmp1);
priority_queue<foo, vector<foo>, decltype(cmp1)> pq1(cmp1);
//Side Note: priority queue is sorted in REVERSE order (largest elements are first)
map<foo, bar, decltype(cmp1)> Map1(cmp1);
struct cmp2
{
bool operator () (const foo& a, const foo& b){return /*comparator function*/;}
};
//Here you do not need to pass the comparator as a parameter
//This makes it easier to create arrays of stl objects
set<foo, cmp2> Set2;
priority_queue<foo, vector<foo>, cmp2> pq2;
map<foo, bar, cmp2> Map2;
set<foo, cmp2> Set2b[100];//array of sets with the comparator
```
#### Example of Comparators for Primitives
Since you cannot overload operators for primitives, you must use custom comparators
<!-- Tested -->
```cpp
const int N = 8;
int a[N], b[N] = {4,8,2,3,4,1,2,4};
int main()
{
printf("--- Comparator 1 ---\n");
iota(a, a+N, 0);
sort(a, a+N, greater<int>());
//sort a in decreasing order
for(int i=0;i<N;++i) printf("a[%d] = %d\n", i, a[i]);
printf("--- Comparator 2 ---\n");
iota(a, a+N, 0);
sort(a, a+N, [](int x, int y){return b[x]<b[y]||(b[x]==b[y]&&x>y);});
//sort a by increasing values of b[i], breaking ties by decreasing index
for(int i=0;i<N;++i) printf("a[%d] = %d: b[%d] = %d\n", i, a[i], a[i], b[a[i]]);
}
```
Output:
```
--- Comparator 1 ---
a[0] = 7
a[1] = 6
a[2] = 5
a[3] = 4
a[4] = 3
a[5] = 2
a[6] = 1
a[7] = 0
--- Comparator 2 ---
a[0] = 5: b[5] = 1
a[1] = 6: b[6] = 2
a[2] = 2: b[2] = 2
a[3] = 3: b[3] = 3
a[4] = 7: b[7] = 4
a[5] = 4: b[4] = 4
a[6] = 0: b[0] = 4
a[7] = 1: b[1] = 8
```
Comments: Comparator 1 sorts array $a$ in decreasing order. Comparator 2 sorts array $a$ in increasing $b[a[i]]$ value, breaking ties with decreasing index
If we instead wanted to sort in descending order, this is also very simple. Instead of the `cmp` function returning `return a.height < b.height;`, it should do `return a.height > b.height;`.
### Java
Java has built-in functions for sorting: `Arrays.sort(arr)` for arrays, and `Collections.sort(list)` for `ArrayLists`. However, if we use custom objects, or if we want to sort elements in a different order, then we'll need to use a `Comparator`.`
Normally, sorting functions rely on moving objects with a lower value ahead of objects with a higher value if sorting in ascending order, and vice versa if in descending order. This is done through comparing two objects at a time. What a `Comparator` does is compare two objects as follows, based on our comparison criteria:
- If object $x$ is less than object $y$, return a negative number
- If object $x$ is greater than object $y$, return a positive number
What a `Comparator` does is compare two objects as follows, based on our comparison criteria:
- If object $x$ is less than object $y$, return a negative number.
- If object $x$ is greater than object $y$, return a positive number.
- If object $x$ is equal to object $y$, return 0.
In addition to returning the correct number, comparators should also satisfy the following conditions:
@ -201,7 +72,7 @@ In addition to returning the correct number, comparators should also satisfy the
Java has default functions for comparing `int`, `long`, `double` types. The `Integer.compare()`, `Long.compare()`, and `Double.compare()` functions take two arguments $x$ and $y$ and compare them as described above.
Now, there are two ways of implementing this in Java: `Comparable`, and `Comparator`. They essentially serve the same purpose, but `Comparable` is generally easier and shorter to code. `Comparable` is a function implemented within the class containing the custom object, while \mintinline{java}{Comparator} is its own class. For our example, we'll use a `Person` class that contains a person's height and weight, and sort in ascending order by height.
Now, there are two ways of implementing this in Java: `Comparable`, and `Comparator`. They essentially serve the same purpose, but `Comparable` is generally easier and shorter to code. `Comparable` is a function implemented within the class containing the custom object, while `Comparator` is its own class. For our example, we'll use a `Person` class that contains a person's height and weight, and sort in ascending order by height.
If we use `Comparable`, we'll need to put `implements Comparable<Person>` into the heading of the class. Furthermore, we'll need to implement the `compareTo` method. Essentially, `compareTo(x)` is the `compare` function that we described above, with the object itself as the first argument, or `compare(self, x)`.
@ -216,6 +87,7 @@ static class Person implements Comparable<Person>{
}
}
```
When using Comparable, we can just call `Arrays.sort(arr)` or `Collections.sort(list)` on the array or list as usual.
If instead we choose to use `Comparator`, we'll need to declare a second `Comparator` class, and then implement that:
@ -233,14 +105,78 @@ static class Comp implements Comparator<Person>{
}
}
```
### Python
#### Comparator in Sorting
When using `Comparator`, the syntax for using the built-in sorting function requires a second argument: `Arrays.sort(arr, new Comp())`, or `Collections.sort(list, new Comp())`.
There are three main ways to implement a custom comparator in python
If we instead wanted to sort in descending order, this is also very simple. Instead of the comparing function returning `Integer.compare(x, y)` of the arguments, it should instead return `-Integer.compare(x, y)`.
## Sorting by Multiple Criteria
Now, suppose we wanted to sort a list of `Person`s in ascending order, primarily by height and secondarily by weight. We can do this quite similarly to how we handled sorting by one criterion earlier. What the comparator function needs to do is to compare the weights if the heights are equal, and otherwise compare heights, as that's the primary sorting criterion.
### C++
```cpp
bool cmp(Person a, Person b) {
if(a.height == b.height) {
return a.weight < b.weight;
}
return a.height < b.height;
}
int main() {
sort(arr, arr+N, cmp); // sorts the array in ascending order by height and then weight if the heights are equal
}
```
Sorting with an arbitrary number of criteria is done similarly.
### Java
```java
static class Person implements Comparable<Person>{
int height, weight;
public Person(int h, int w){
height = h; weight = w;
}
public int compareTo(Person p){
if(height == p.height){
return Integer.compare(weight, p.weight);
} else {
return Integer.compare(height, p.height);
}
}
}
```
Sorting with an arbitrary number of criteria is done similarly.
An alternative way of representing custom objects is with arrays. Instead of using a custom object to store data about each person, we can simply use `int[]`, where each `int` array is of size 2, and stores pairs of {height, weight}, probably in the form of a list like `ArrayList<int[]>`. Since arrays aren't objects in the usual sense, we need to use `Comparator`. Example for sorting by the same two criteria as above:
```java
static class Comp implements Comparator<int[]>{
public int compare(int[] a, int[] b){
if(a[0] == b[0]){
return Integer.compare(a[1], b[1]);
} else {
return Integer.compare(a[0], b[0]);
}
}
}
```
I don't recommend using arrays to represent objects mostly because it's confusing, but it's worth noting that some competitors do this.
## Python
(merge w/ sections above?)
### Operator Overloading
Type 1) Operator Overloading
- Overloads operators
<!-- Tested -->
```py
class Foo:
@ -255,43 +191,15 @@ for i in range(8):
print(*a)
print(*sorted(a))
```
Output:
```
Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8)
Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9)
```
Type 2) Function/Lambda
- This method defines how to compare two elements represented by an integer
- Positive: First term is greater than the second term
- Zero: First term and second term are equal
- Negative: First term is less than the second term
<!-- Tested -->
```py
from functools import cmp_to_key
class Foo:
def __init__(self, _Bar): self.Bar = _Bar
def __str__(self): return "Foo({})".format(self.Bar)
a = []
for i in range(8):
a.append(Foo(random.randint(0, 9)))
print(*a)
print(*sorted(a, key=cmp_to_key(lambda foo1, foo2: foo1.Bar - foo2.Bar)))
def cmp(foo1, foo2):
return foo1.Bar - foo2.Bar
print(*sorted(a, key=cmp_to_key(cmp)))
```
Output:
```
Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8)
Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9)
Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9)
```
Type 3) Remapping Key
### Remapping Key
- This method maps an object to another comparable datatype with which to be sorted. In this case, `Foo` is sorted by the sum of its members `x` and `y`.
<!-- Tested -->
@ -310,9 +218,58 @@ def key(foo):
return foo.Bar + foo.Baz
print(*sorted(a, key=key))
```
Output:
```
Foo(10,2) Foo(30,2) Foo(60,6) Foo(90,7) Foo(80,7) Foo(80,9) Foo(60,9) Foo(90,8)
Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8)
Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8)
```
### Function / Lambda
- This method defines how to compare two elements represented by an integer
- Positive: First term is greater than the second term
- Zero: First term and second term are equal
- Negative: First term is less than the second term
Note how the comparator must be converted to a `key`.
<!-- Tested -->
```py
from functools import cmp_to_key
class Foo:
def __init__(self, _Bar): self.Bar = _Bar
def __str__(self): return "Foo({})".format(self.Bar)
a = []
for i in range(8):
a.append(Foo(random.randint(0, 9)))
print(*a)
print(*sorted(a, key=cmp_to_key(lambda foo1, foo2: foo1.Bar - foo2.Bar)))
def cmp(foo1, foo2):
return foo1.Bar - foo2.Bar
print(*sorted(a, key=cmp_to_key(cmp)))
```
Output:
```
Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8)
Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9)
Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9)
```
## Problems
- [Lifeguards](http://usaco.org/index.php?page=viewproblem2&cpid=786)
- [Rental Service](http://usaco.org/index.php?page=viewproblem2&cpid=787)
- [Mountains](http://usaco.org/index.php?page=viewproblem2&cpid=896)
- [Mooyo Mooyo](http://www.usaco.org/index.php?page=viewproblem2&cpid=860)
- Not a sorting problem, but you can use sorting to simulate gravity nicely.
- Write a custom comparator (read below) which puts zeroes at the front and use `stable_sort` to keep the relative order of other elements the same.
- [Meetings](http://www.usaco.org/index.php?page=viewproblem2&cpid=967)
- hard!

View file

@ -0,0 +1,295 @@
---
id: sorting-cpp
title: "More on Sorting in C++"
author: Siyong Huang, Michael Cao, Darren Yao, Benjamin Qi
prerequisites:
-
- Bronze - Introduction to Graphs
-
- Silver - Sorting with Custom Comparators
---
More information about sorting with custom comparators in C++. Some overlap with the previous article.
<!-- END DESCRIPTION -->
## Additional Resources
- [fushar (C++)](http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/)
## An Example with Graphs
Consider a graph with weighted edges (pairs of values), such as in the problem [Wormhole Sort (Silver)](http://www.usaco.org/index.php?page=viewproblem2&cpid=992). There are multiple ways to solve this problem, but all of them start by sorting the edges in nondecreasing order of weight. If we only stored the edge weights and sorted them, we would have a sorted list of edge weights, but it would be impossible to tell which weights corresponded to which edges. However, if we create a class representing the edges and define a custom comparator to sort them by weight, we can sort the edges in ascending order while also keeping track of their endpoints.
## Comparators for Sorting
There are 2 main ways to have a custom comparator in c++.
### Overloading Operator
[Why const T&?](https://stackoverflow.com/questions/11805322/why-should-i-use-const-t-instead-of-const-t-or-t)
- Pro:
- This is the easiest to implement
- Easy to work with STL
- Con:
- Only works for objects (not primitives)
- Only supports two types of comparisons (less than (<) and greater than (>))
<!-- Tested -->
```cpp
struct Foo
{
int Bar;
Foo(int _Bar=-1):Bar(_Bar){}
bool operator < (const Foo& foo2) const {return Bar < foo2.Bar;}
};
const int N = 8;
int main()
{
Foo a[N];
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
}
/**
Output:
(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0)
(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18)
*/
```
In the context of Wormhole Sort (uses [friend](https://www.geeksforgeeks.org/friend-class-function-cpp/)):
```cpp
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int a,b,w;
friend bool operator<(const Edge& x, const Edge& y) { return x.w < y.w; }
}; // a different way to write less than
int main() {
int M = 4;
vector<Edge> v;
for (int i = 0; i < M; ++i) {
int a,b,w; cin >> a >> b >> w;
v.push_back({a,b,w});
}
sort(begin(v),end(v));
for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n";
}
/*
Input:
1 2 9
1 3 7
2 3 10
2 4 3
*/
/*
Output:
2 4 3
1 3 7
1 2 9
2 3 10
*/
```
### Function
- Pro:
- Works for both objects and primitives
- Supports many different comparators for the same object
- Con:
- More difficult to implement
- Extra care needs to be taken to support STL
We can also use [lambda expressions](https://www.geeksforgeeks.org/lambda-expression-in-c/) in C++11 or above.
<!-- Tested -->
```cpp
struct Foo
{
int Bar;
Foo(int _Bar=-1):Bar(_Bar){}
};
const int N = 8;
Foo a[N];
bool cmp1(Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;}
function<void(Foo,Foo)> cmp2 = [](Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;}; // lambda expression
// void(Foo,Foo) means that the function takes in two parameters of type Foo and returns void
// "function<void(Foo,Foo)>"" can be replaced with "auto"
int main()
{
printf("--- Method 1 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, [](Foo foo1, Foo foo2){return foo1.Bar<foo2.Bar;}); // lambda expression
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
printf("--- Method 2 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, cmp1);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
printf("--- Method 3 ---\n");
for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20));
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
sort(a, a+N, cmp2);
for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n");
}
```
Output:
```
--- Method 1 ---
(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0)
(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18)
--- Method 2 ---
(Foo: 5) (Foo: 13) (Foo: 18) (Foo: 0) (Foo: 9) (Foo: 4) (Foo: 2) (Foo: 15)
(Foo: 0) (Foo: 2) (Foo: 4) (Foo: 5) (Foo: 9) (Foo: 13) (Foo: 15) (Foo: 18)
--- Method 3 ---
(Foo: 1) (Foo: 1) (Foo: 18) (Foo: 0) (Foo: 11) (Foo: 12) (Foo: 1) (Foo: 5)
(Foo: 0) (Foo: 1) (Foo: 1) (Foo: 1) (Foo: 5) (Foo: 11) (Foo: 12) (Foo: 18)
```
In the context of Wormhole Sort:
```cpp
#include <bits/stdc++.h>
using namespace std;
struct Edge { int a,b,w; };
int main() {
int M = 4;
vector<Edge> v;
for (int i = 0; i < M; ++i) {
int a,b,w; cin >> a >> b >> w;
v.push_back({a,b,w});
}
sort(begin(v),end(v),[](const Edge& x, const Edge& y) { return x.w < y.w; });
for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n";
}
```
### Comparators for STL
Operator overloading works as expected for using in STL. If you are sorting elements in reverse order, you can use the STL [greater](https://en.cppreference.com/w/cpp/utility/functional/greater) comparator instead.
For function comparators, some extra care needs to be taken:
<!-- Reasonably Tested -->
```cpp
struct foo
{
//members
};
auto cmp1 = [](const foo& a, const foo& b){return /*comparator function*/;};
//This way is shorter to write, but don't forget to pass the comparator as a parameter in the constructor!
//If you need to create an array of the objects, I would either use pointers of the method shown below
set<foo, decltype(cmp1)> Set1(cmp1);
priority_queue<foo, vector<foo>, decltype(cmp1)> pq1(cmp1);
//Side Note: priority queue is sorted in REVERSE order (largest elements are first)
map<foo, bar, decltype(cmp1)> Map1(cmp1);
struct cmp2
{
bool operator () (const foo& a, const foo& b){return /*comparator function*/;}
};
//Here you do not need to pass the comparator as a parameter
//This makes it easier to create arrays of stl objects
set<foo, cmp2> Set2;
priority_queue<foo, vector<foo>, cmp2> pq2;
map<foo, bar, cmp2> Map2;
set<foo, cmp2> Set2b[100];//array of sets with the comparator
```
### Example of Comparators for Primitives
Since you cannot overload operators for primitives, you must use custom comparators.
<!-- Tested -->
```cpp
const int N = 8;
int a[N], b[N] = {4,8,2,3,4,1,2,4};
int main() {
printf("--- Comparator 1 ---\n");
iota(a, a+N, 0); // a={0,1,2,3,4,5,6,7}
sort(a, a+N, greater<int>());
//sort a in decreasing order
for(int i=0;i<N;++i) printf("a[%d] = %d\n", i, a[i]);
printf("--- Comparator 2 ---\n");
iota(a, a+N, 0);
sort(a, a+N, [](int x, int y){return b[x]<b[y]||(b[x]==b[y]&&x>y);});
//sort a by increasing values of b[i], breaking ties by decreasing index
for(int i=0;i<N;++i) printf("a[%d] = %d: b[%d] = %d\n", i, a[i], a[i], b[a[i]]);
}
/*
Output:
--- Comparator 1 ---
a[0] = 7
a[1] = 6
a[2] = 5
a[3] = 4
a[4] = 3
a[5] = 2
a[6] = 1
a[7] = 0
--- Comparator 2 ---
a[0] = 5: b[5] = 1
a[1] = 6: b[6] = 2
a[2] = 2: b[2] = 2
a[3] = 3: b[3] = 3
a[4] = 7: b[7] = 4
a[5] = 4: b[4] = 4
a[6] = 0: b[0] = 4
a[7] = 1: b[1] = 8
*/
```
Comments: Comparator 1 sorts array $a$ in decreasing order. Comparator 2 sorts array $a$ in increasing $b[a[i]]$ value, breaking ties by placing the greater index first.
## Sorting Pairs
An alternative way of representing custom objects is with the data structure `pair<int, int>`. In the above example, instead of creating a `struct`, we can simply declare an array of pairs. The sort function automatically uses the first element of the pair for comparison and the second element as a secondary point of comparison:
```cpp
pair<int, int> arr[N];
int main() {
sort(arr, arr+N);
}
```
There is no need to create a custom comparator.
```cpp
#include <bits/stdc++.h>
using namespace std;
#define f first
#define s second
int main() {
int M = 4;
vector<pair<int,pair<int,int>>> v;
for (int i = 0; i < M; ++i) {
int a,b,w; cin >> a >> b >> w;
v.push_back({w,{a,b}});
}
sort(begin(v),end(v));
for (auto e: v) cout << e.s.f << " " << e.s.s << " " << e.f << "\n";
}
```

View file

@ -23,9 +23,12 @@ Compute shortest paths where all edge weights are 1.
### Problems
- [CSAcademy BFS-DFS](https://csacademy.com/contest/round-41/task/bfs-dfs/) [](50)
- [Cow Navigation](http://www.usaco.org/index.php?page=viewproblem2&cpid=695)
- [Dream](http://www.usaco.org/index.php?page=viewproblem2&cpid=575)
- bad problem ...
- [Lasers](http://www.usaco.org/index.php?page=viewproblem2&cpid=671)
- [Monsters](https://cses.fi/problemset/task/1194)
- USACO
- [Monsters](https://cses.fi/problemset/task/1194)
- [Cow Navigation](http://www.usaco.org/index.php?page=viewproblem2&cpid=695)
- [Dream](http://www.usaco.org/index.php?page=viewproblem2&cpid=575)
- bad problem ...
- [Lasers](http://www.usaco.org/index.php?page=viewproblem2&cpid=671)
- [Visit FJ](http://www.usaco.org/index.php?page=viewproblem2&cpid=717)
- Other
- [CSAcademy BFS-DFS](https://csacademy.com/contest/round-41/task/bfs-dfs/) [](50)

View file

@ -7,7 +7,7 @@ prerequisites:
- Gold - Shortest Paths
---
Disjoint Set Union and Minimum Spanning Trees
**Disjoint Set Union** and **Minimum Spanning Trees**
<!-- END DESCRIPTION -->

View file

@ -40,6 +40,7 @@ See my implementation [here](https://github.com/bqi343/USACO/blob/master/Impleme
- [DMOJ Continued Fractions](https://dmoj.ca/problem/dmopc19c7p4)
- [USACO Plat - NonDec](http://www.usaco.org/index.php?page=viewproblem2&cpid=997)
- [JOI Secret](https://oj.uz/problem/view/JOI14_secret)
## Segment Tree

View file

@ -34,19 +34,21 @@ Lowest Common Ancestor and other tree topics.
USACO:
- [USACO Plat Max Flow](http://www.usaco.org/index.php?page=viewproblem2&cpid=576)
- [USACO Gold Milk Visits](http://www.usaco.org/index.php?page=viewproblem2&cpid=970)
- [USACO Gold Cow Land](http://www.usaco.org/index.php?page=viewproblem2&cpid=921)
- LCA + BIT
- [USACO Plat Promote](http://www.usaco.org/index.php?page=viewproblem2&cpid=696)
- Subtree + BIT
- [USACO Plat Disrupt](http://www.usaco.org/index.php?page=viewproblem2&cpid=842)
- HLD is possible, but just do binary jumps
- [USACO Plat Tree Boxes](http://www.usaco.org/index.php?page=viewproblem2&cpid=948)
- interactive!!
- [USACO Plat Gathering](http://www.usaco.org/index.php?page=viewproblem2&cpid=866)
- [USACO Plat Exercise](http://www.usaco.org/index.php?page=viewproblem2&cpid=901)
- tricky
- Gold
- [USACO Gold Milk Visits](http://www.usaco.org/index.php?page=viewproblem2&cpid=970)
- [USACO Gold Cow Land](http://www.usaco.org/index.php?page=viewproblem2&cpid=921)
- LCA + BIT
- Plat
- [Max Flow](http://www.usaco.org/index.php?page=viewproblem2&cpid=576)
- [Promote](http://www.usaco.org/index.php?page=viewproblem2&cpid=696)
- Subtree + BIT
- [Disrupt](http://www.usaco.org/index.php?page=viewproblem2&cpid=842)
- HLD is possible, but just do binary jumps
- [Tree Boxes](http://www.usaco.org/index.php?page=viewproblem2&cpid=948)
- interactive!!
- [Gathering](http://www.usaco.org/index.php?page=viewproblem2&cpid=866)
- [Exercise](http://www.usaco.org/index.php?page=viewproblem2&cpid=901)
- tricky
Other:

View file

@ -31,22 +31,30 @@ const ModuleOrdering = {
},
"simulation",
"complete-search",
"intro-graphs",
],
"silver": [
"time-comp",
"greedy",
{
name: "Sorting",
items: [
"intro-sorting",
"sorting-custom",
"sorting-cpp",
]
},
"binary-search",
"2P",
"data-structures",
"greedy",
"prefix-sums",
"dfs"
{
name: "Graphs",
items: [
"dfs",
"cyc",
]
}
],
"gold": [
{