-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
CompetitiveProgramming/2021_10_27_CPClass-6/Codes/SOS_dp.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Problem link : https://codeforces.com/contest/165/problem/E | ||
|
||
#include <bits/stdc++.h> | ||
using namespace std; | ||
|
||
const int limit = 22; | ||
|
||
vector<int> dp(1 << limit , -1); | ||
|
||
int change(int x) { | ||
int num = 0; | ||
for(int i = 0 ; i < limit ; i++) { | ||
if(x & (1 << i)) continue; | ||
num += (1 << i); | ||
} | ||
return num; | ||
} | ||
|
||
int main() { | ||
int n; | ||
cin >> n; | ||
vector<int> a(n); | ||
for(int i = 0 ; i < n ; i++) { | ||
cin >> a[i]; | ||
dp[change(a[i])] = a[i]; | ||
// cout << bitset<limit>(a[i]) << " " << bitset<limit>(a[i] ^ (~0)) << "\n"; | ||
} | ||
|
||
for(int i = 0 ; i < limit ; i++) { | ||
for(int mask = (1 << limit) - 1 ; mask >= 0 ; mask--) { | ||
if(mask & (1 << i)) continue; | ||
dp[mask] = max(dp[mask] , dp[mask | (1 << i)]); | ||
} | ||
} | ||
|
||
for(int i = 0 ; i < n ; i++) { | ||
cout << dp[a[i]] << " "; | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
CompetitiveProgramming/2021_10_27_CPClass-6/Codes/graph_traversals.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#include <bits/stdc++.h> | ||
using namespace std; | ||
|
||
int n; | ||
vector<vector<int>> a; //adjacency matrix | ||
vector<vector<pair<int , int>>> adj; // adjacency matrix for weighted graphs. | ||
vector<int> visited; | ||
|
||
void dfs_graph(int s) { | ||
if(visited[s]) return; | ||
visited[s] = 1; | ||
|
||
for(int it : a[s]) { | ||
dfs_graph(it); | ||
} | ||
} | ||
|
||
/* | ||
Since trees are special cases of graphs, we don't need to maintain visited array for them | ||
*/ | ||
void dfs_tree(int s , int p) { | ||
for(int it : a[s]) { | ||
if(it != p) { | ||
dfs_tree(it , s); | ||
} | ||
} | ||
} | ||
|
||
void single_source_bfs(int s) { | ||
queue<int> q; | ||
q.push(s); | ||
|
||
vector<int> visited(n) , d(n , INT_MAX); | ||
visited[s] = 1; | ||
d[s] = 0; | ||
|
||
while(!q.empty()) { | ||
int x = q.front(); | ||
q.pop(); | ||
|
||
for(int it : a[x]) { | ||
if(visited[it]) continue; | ||
visited[it] = 1; | ||
d[it] = d[x] + 1; | ||
q.push(it); | ||
} | ||
} | ||
} | ||
|
||
void multi_source_bfs(vector<int> sources) { | ||
queue<int> q; | ||
vector<int> visited(n) , d(n , INT_MAX); | ||
|
||
for(int s : sources) { | ||
q.push(s); | ||
visited[s] = 1; | ||
d[s] = 0; | ||
} | ||
|
||
while(!q.empty()) { | ||
int x = q.front(); | ||
q.pop(); | ||
|
||
for(int it : a[x]) { | ||
if(visited[it]) continue; | ||
visited[it] = 1; | ||
d[it] = d[x] + 1; | ||
q.push(it); | ||
} | ||
} | ||
} | ||
|
||
void bfs01(int s) { | ||
deque<int> q; | ||
vector<int> visited(n) , d(n , INT_MAX); | ||
q.push_back(s); | ||
visited[s] = 1; | ||
d[s] = 0; | ||
|
||
while(!q.empty()) { | ||
int x = q.front(); | ||
q.pop_front(); | ||
|
||
for(pair<int ,int> p : adj[x]) { | ||
/* | ||
p.first -> node | ||
p.second -> weight of edge [x , p.first]. Can only be 0 or 1. | ||
*/ | ||
if(visited[p.first]) continue; | ||
visited[p.first] = 1; | ||
if(p.second) { | ||
d[p.first] = d[x] + 1; | ||
q.push_back(p.first); | ||
} | ||
else { | ||
d[p.first] = d[x]; | ||
q.push_front(p.first); | ||
} | ||
} | ||
} | ||
} | ||
|
||
int main() { | ||
int m; | ||
cin >> n >> m; | ||
a.resize(n); | ||
adj.resize(n); | ||
|
||
for(int i = 0 ; i < m ; i++) { | ||
int x , y , w; | ||
cin >> x >> y >> w; | ||
a[x].push_back(y); | ||
a[y].push_back(x); | ||
|
||
adj[x].push_back({y , w}); | ||
adj[y].push_back({x , w}); | ||
} | ||
|
||
dfs_graph(0); | ||
dfs_tree(0 , -1); | ||
|
||
bfs01(0); | ||
single_source_bfs(0); | ||
multi_source_bfs(vector<int>{0 , 1}); | ||
} |
Binary file added
BIN
+350 KB
CompetitiveProgramming/2021_10_27_CPClass-6/Competitive_Programming_Class_6.pdf
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Competitive Programming Class 6 | ||
|
||
### Get the presentation for ```Introduction to Graphs``` used in class: [Here](./Competitive_Programming_Class_6.pdf) | ||
### Access the content for ```SOS DP``` used in class : [Here](./SOS_DP.md) | ||
|
||
## Get the class recording: [Here](https://drive.google.com/file/d/1yGWzGBHUtJ0pmGWQtlLeVh4GpwkTXHnB/view?usp=sharing) | ||
|
||
#### October 27, 2021 | ||
|
||
<hr> | ||
|
||
## Class Coverage | ||
|
||
- SOS DP | ||
- Introduction to Graphs | ||
- Graph representations | ||
- Graph Traversal Basic Intro (Implementation details couldn't be discussed due to time shortage so will be discussed in the next class). | ||
|
||
## Additional Resources | ||
|
||
- [SOS DP](https://codeforces.com/blog/entry/45223) | ||
- [Submask Enumeration](https://cp-algorithms.com/algebra/all-submasks.html) | ||
- [Graph Algorithms](https://codeforces.com/blog/entry/16221) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Sum over Subsets Dynamic Programming | ||
|
||
## Pre-requisites | ||
- Dynamic Programming | ||
- Bitmasks | ||
|
||
|
||
## Introduction | ||
|
||
SOS DP is one of the most feared yet one of the most beautiful techniques out there. It helps solve problems involving bitmasks which are out of the scope of regular bitmask dp. To understand the technique we'll use a problem. We'll figure out the shortcomings of the brute force and optimize it using the observations we make. Just like Knapsack problem, focus on the details of the technique rather than focusing on the details of the problem. Ok so lets start. | ||
|
||
## Problem | ||
|
||
``` | ||
Given an array A of integers of size N, we need to calculate ∀x ∈ [0 , 2^N) function F(x) = Sum of all the subsets of the subset of A represented by the bitmask x. In other words if G(x) = ΣA[i] ∀i such that ith bit is set in x, then F(x) = ΣG(i) ∀i such that x&i == i i.e., i is a sub mask of x. | ||
``` | ||
|
||
In simpler words we need to find the array B of size 2^N, where B[i] = Sum of all the subsets of the ith subset of A. | ||
|
||
For ease of further calculations we'll pre calculate the array G as follows: | ||
|
||
```c++ | ||
vector<int> G((1 << N) , 0); | ||
for(int mask = 0 ; mask < (1 << N) ; mask++) { | ||
for(int i = 0 ; i < N ; i++) { | ||
if(mask & (1 << i)) { | ||
G[mask] += A[i]; | ||
} | ||
} | ||
} | ||
``` | ||
## Brute Force | ||
The most naive approach to this problem is simply doing what the problem asks, i.e., iterate over all the masks in the domain. For each mask, iterate over all the masks again. If the inner mask is the sub mask of the outer mask, then add its contents to the answer. | ||
#### Code | ||
```c++ | ||
for(int outer_mask = 0 ; outer_mask < (1<<N) ; ++outer_mask){ | ||
for(int inner_mask = 0 ; inner_mask < (1<<N) ; ++inner_mask){ | ||
// checking if i is sub mask of mask | ||
if((ouer_mask & inner_mask) == inner_mask){ | ||
dp[outer_mask] += G[inner_mask]; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
#### Complexity analysis | ||
|
||
It's quite straightforward. Two nested loops are used so the complexity is O(2^N * 2^N) = O(4^N). | ||
|
||
## Shortcomings of Brute Force | ||
|
||
For every mask we are going over all the masks again and again and using if condition we are checking if we need to use them. If somehow we only iterate over the required submasks for each mask, then we can avoid a lot of unnecessary loop iterations. | ||
|
||
## Sub optimal solution | ||
|
||
Using the above shortcoming the following code would be more optimal then the brute force. | ||
|
||
```c++ | ||
// Pseudo code | ||
for(int mask = 0 ; mask < (1<<N) ; ++mask){ | ||
for(sub_masks of mask){ | ||
dp[mask] += G[i]; | ||
} | ||
} | ||
``` | ||
|
||
So how do we generate all the submasks efficiently? We can do it in the following way: | ||
|
||
```c++ | ||
for(int sub_mask = mask ; ; sub_mask = (sub_mask - 1) & mask) { | ||
// Use sub mask | ||
if(!sub_mask) break; | ||
} | ||
``` | ||
|
||
#### Why does this work? | ||
Well this is because subtracting 1 from the the ```sub_mask``` unsets the leftmost set bit and sets all the zeroes after it. Using ```& mask``` we make sure that the new number obtained is indeed a sub mask of ```mask```. | ||
|
||
#### Complexity Analysis | ||
There are ```C(n , k)``` masks having ```k``` set bits. For each of such masks there are ```2^k``` sub masks for them. So the total number of iterations are as follows: | ||
|
||
``` | ||
Σ (C(n , k) * 2^k) for each k from 0 to N. | ||
``` | ||
|
||
This expression evaluates to (1 + 2)^N = ```3^N``` using ```binomial theorem```. | ||
|
||
|
||
## Shortcomings in the sub optimal solution | ||
|
||
A noticeable flaw in our previous approach is that an index A[x] with x having K off bits is visited by 2^K masks. Thus there is repeated recalculation. We can avoid this repeated calculation using a smart DP, which is called SOS DP. | ||
|
||
## SOS DP | ||
|
||
Lets define another function ```fun(mask , i)``` which returns the sum of all the subsets of mask such that the submasks only differ in the first (i+1) bits. | ||
|
||
The transitions would be as follows: | ||
|
||
```c++ | ||
if(i == -1) { | ||
return G[mask]; | ||
} | ||
if(mask & (1 << i)) { | ||
// ith bit is set | ||
return fun(mask , i) = fun(mask , i-1) + fun(mask ^ (1 << i) , i-1); | ||
} | ||
else { | ||
return fun(mask , i) = fun(mask , i-1); | ||
} | ||
``` | ||
<img src='assets/SOS_DP_recursive_tree.png'> | ||
#### Complexity Analysis | ||
As already discussed complexity of DP problems = ```O(number of states * complexity of one transition)```. Thus time complexity of SOS DP is ```O(N * 2^N)```. | ||
## Space optimised solution | ||
If the order in which states are visited is optimal then we don't even need the second state in our dp array. | ||
```c++ | ||
for(int i = 0 ; i < (1 << N) ; ++i) | ||
dp[i] = G[i]; | ||
for(int i = 0 ; i < N ; ++i) | ||
for(int mask = 0 ; mask < (1 << N) ; ++mask) { | ||
if(mask & (1<<i)) | ||
dp[mask] += F[mask^(1<<i)]; | ||
} | ||
``` | ||
|
||
This helps us achieve a space complexity of ```O(2^N)```. | ||
|
||
## Practice Problem | ||
|
||
[Compatible Numbers](https://codeforces.com/contest/165/problem/E) | ||
|
||
## Links for further studying | ||
|
||
- [CF blog](https://codeforces.com/blog/entry/45223) | ||
- [Submask enumeration](https://cp-algorithms.com/algebra/all-submasks.html) |
Binary file added
BIN
+26.5 KB
CompetitiveProgramming/2021_10_27_CPClass-6/assets/SOS_DP_recursive_tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters