Skip to content

Commit

Permalink
Added content for CP class 6
Browse files Browse the repository at this point in the history
  • Loading branch information
embiway committed Oct 27, 2021
1 parent b544260 commit 02fdb75
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 0 deletions.
39 changes: 39 additions & 0 deletions CompetitiveProgramming/2021_10_27_CPClass-6/Codes/SOS_dp.cpp
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 CompetitiveProgramming/2021_10_27_CPClass-6/Codes/graph_traversals.cpp
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 not shown.
23 changes: 23 additions & 0 deletions CompetitiveProgramming/2021_10_27_CPClass-6/README.md
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)
146 changes: 146 additions & 0 deletions CompetitiveProgramming/2021_10_27_CPClass-6/SOS_DP.md
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)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions CompetitiveProgramming/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Class 3 (Introduction to Dynamic Programming): held on August 08, 2021](2021_08_08_CPClass-3)
- [Class 4 (Problems on Dynamic Programming): held on August 15, 2021](2021_08_15_CPClass-4)
- [Class 5 (DP tricks and Bitmask DP): held on August 21, 2021](2021_08_21_CPClass-5)
- [Class 6 (SOS DP and Intro to Graphs): held on October 27, 2021](2021_10_27_CPClass-6)

## Reading Resources

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ If you are a part of MNNIT join us on Microsoft Team [MNNIT CC Queries Official]
- [Competitive Programming Class-3](CompetitiveProgramming/2021_08_08_CPClass-3)
- [Competitive Programming Class-4](CompetitiveProgramming/2021_08_15_CPClass-4)
- [Competitive Programming Class-5](CompetitiveProgramming/2021_08_21_CPClass-5)
- [Competitive Programming Class-6](CompetitiveProgramming/2021_10_27_CPClass-6)

- [Git/GitHub classes](Git-GitHub)
- [Git Class-1](Git-GitHub/2021_04_20_GitClass-1)
Expand Down

0 comments on commit 02fdb75

Please sign in to comment.