Skip to content

Commit 737ca7e

Browse files
Merge pull request #12 from sathiiii/master
Added new solutions to Hackerrank/Algorithms
2 parents ba86624 + 1bdd933 commit 737ca7e

25 files changed

Lines changed: 1075 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright (C) 2020, Sathira Silva.
3+
4+
Problem Statement: You and your friend decide to play a game using a stack consisting of N bricks. In this game, you can
5+
alternatively remove 1, 2 or 3 bricks from the top, and the numbers etched on the removed bricks are
6+
added to your score. You have to play so that you obtain the maximum possible score. It is given that your
7+
friend will also play optimally and you make the first move.
8+
*/
9+
10+
#include <bits/stdc++.h>
11+
#include <numeric>
12+
13+
using namespace std;
14+
15+
vector<string> split_string(string);
16+
17+
long long bricksGame(vector<int> arr) {
18+
int n = arr.size();
19+
long long score[n];
20+
if (arr.size() <= 3)
21+
return accumulate(arr.begin(), arr.end(), 0);
22+
// Base Cases:
23+
score[n - 1] = arr[n - 1];
24+
score[n - 2] = score[n - 1] + arr[n - 2];
25+
score[n - 3] = score[n - 2] + arr[n - 3];
26+
long long sum = score[n - 3];
27+
// Build the optimal solution using the optimal solutions of it's subproblems.
28+
// Traverse the array backwards and get the cumulative sum at each state.
29+
for (int i = n - 4; i >= 0; i--) {
30+
sum += arr[i];
31+
score[i] = sum - min({score[i + 1], score[i + 2], score[i + 3]});
32+
}
33+
return score[0];
34+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Problem Statement: Given a tree T with n nodes, how many subtrees (T') of T have at most K edges connected to (T - T')?
3+
4+
Approach: The idea is to depth first traverse the tree and count the subtrees having number of edges connected to the complement
5+
of the current subtree upto k edges.
6+
*/
7+
8+
#include <bits/stdc++.h>
9+
10+
using namespace std;
11+
12+
vector<string> split_string(string);
13+
14+
vector<int> tree[51];
15+
long long dp[51][51];
16+
int k;
17+
18+
void dfs(int u, int parent = -1) {
19+
for (int v: tree[u]) {
20+
if (v == parent)
21+
continue;
22+
dfs(v, u);
23+
for (int j = k; j > 0; j--) {
24+
for (int x = j; x > 0; x--)
25+
dp[u][j] += dp[u][j - x] * dp[v][x];
26+
dp[u][j] += dp[u][j - 1];
27+
}
28+
}
29+
return;
30+
}
31+
32+
long long cutTree(int n, vector<vector<int>> edges) {
33+
for (auto e: edges) {
34+
tree[e[0]].push_back(e[1]);
35+
tree[e[1]].push_back(e[0]);
36+
}
37+
long long subtrees = 0;
38+
memset(dp, 0, sizeof(dp));
39+
for(int u = 1; u <= n; u++)
40+
dp[u][0] = 1; // Base case
41+
dfs(1);
42+
for(int i = 1; i <= n; i++)
43+
for(int j = 0; j < k; j++)
44+
subtrees += dp[i][j];
45+
return subtrees + dp[1][k] + 1;
46+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'''
2+
Copyright (C) 2020, Sathira Silva.
3+
4+
Problem Statement: King Arthur has a large kingdom that can be represented as a tree, where nodes correspond to cities and edges
5+
correspond to the roads between cities. The kingdom has a total of n cities numbered from 1 to n. The King wants to divide his kingdom
6+
between his two children, Reggie and Betty, by giving each of them 0 or more cities; however, they don't get along so he must divide
7+
the kingdom in such a way that they will not invade each other's cities. The first sibling will invade the second sibling's city if
8+
the second sibling has no other cities directly connected to it.
9+
Given a map of the kingdom's n cities, find and print the number of ways King Arthur can divide it between his two children such that
10+
they will not invade each other. As this answer can be quite large, it must be modulo 7 + 10 ^ 9.
11+
12+
Approach: We have to find the number of ways of colouring subtrees such that its root node has the same colour as the subtree.
13+
This can be easily done with Dynamic Programming and DFS traversal to find the subtrees.
14+
'''
15+
16+
from collections import defaultdict
17+
sys.setrecursionlimit(10 ** 6)
18+
19+
mod = 7 + 10 ** 9
20+
21+
def dfs(city, kingdom, visited):
22+
global dp
23+
visited[city] = True
24+
for neighbourCity in kingdom[city]:
25+
if visited[neighbourCity]:
26+
continue
27+
dfs(neighbourCity, kingdom, visited)
28+
dp[city][0] = (dp[city][0] * dp[neighbourCity][1]) % mod
29+
dp[city][1] = (dp[city][1] * (2 * dp[neighbourCity][1] + dp[neighbourCity][0])) % mod
30+
dp[city][1] -= dp[city][0]
31+
32+
def kingdomDivision(n, roads):
33+
global dp
34+
visited = [False] * (n + 1)
35+
kingdom = defaultdict(list)
36+
for u, v in roads:
37+
kingdom[u].append(v)
38+
kingdom[v].append(u)
39+
dfs(1, kingdom, visited)
40+
return (2 * dp[1][1]) % mod
41+
42+
if __name__ == '__main__':
43+
n = int(stdin.readline())
44+
roads = []
45+
for _ in range(n-1):
46+
roads.append(list(map(int, stdin.readline().rstrip().split())))
47+
dp = [[1, 1] for _ in range(n + 1)]
48+
result = kingdomDivision(n, roads)
49+
stdout.write(str(result) + '\n')
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'''
2+
Copyright (C) 2020, Sathira Silva.
3+
4+
Problem Statement: Given an array of integers and a target sum, determine the sum nearest to but not exceeding
5+
the target that can be created. To create the sum, use any element of your array zero or more times.
6+
7+
Approach: This's a variation of the 0-1 knapsack problem which can be solved easily using Dynamic Programming. Given the capacity of
8+
the knapsack, we have to pack items from the given list of items (and there're infinitely many items from each item)
9+
such that the total size doesn't exceed the capacity of the knapsack.
10+
11+
(01). Subproblem: dp[capacity] for all 0 < capacity <= k
12+
Maximum capacity that can be packed from the items such that the total capacity doesn't exceed i.
13+
(02). Guessing: is item i included or not?
14+
(03). Recurrence: dp[capacity] = max(dp[capacity], items[i] + dp[capacity - items[i]])
15+
(04). Toplogical order: for capacity -> 0,...,k
16+
for item -> 0,...,n - 1
17+
(05). Original problem: dp[k]
18+
19+
Time Complexity: O(n(k + 1)) ~= O(mk) ; m <= n
20+
'''
21+
22+
def unboundedKnapsack(k, items):
23+
# Since the same item can be repeatedly appear in the given list, extract only the unique items. This makes the algorithm
24+
# more efficient.
25+
items = list(set(items))
26+
n = len(items)
27+
dp = [0] * (k + 1)
28+
for capacity in range(k + 1):
29+
for i in range(n):
30+
if items[i] <= capacity:
31+
dp[capacity] = max(dp[capacity], items[i] + dp[capacity - items[i]])
32+
return dp[k]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Problem Statement: https://www.hackerrank.com/challenges/red-john-is-back/problem
3+
4+
Approach: This problem can be solved using Dynamic Programming and Sieve of Eratosthenes (Or any other algorithm for sieving
5+
primes) easily. First we find the number of ways of covering a 4 x n wall with 4 x 1 and 1 x 4 bricks. Then we count the number of
6+
primes less than or equal to that number.
7+
8+
(01). Subproblem: dp[i]
9+
(How many ways are there to cover a 4 x i wall)
10+
(02). Guess: dp[i - 1] and dp[i - 4]
11+
(Should we cover the remaining are with a 4 x 1 brick or a 1 x 4 brick?)
12+
(03). Recurrence: dp[i] = dp[i - 1] + dp[i - 4]
13+
(Base Cases: dp[0] = dp[1] = dp[2] = dp[3] = 1)
14+
(04). Toplogical Order: i from 4 to n
15+
(05). Original Problem: dp[n]
16+
*/
17+
18+
#include <bits/stdc++.h>
19+
20+
using namespace std;
21+
22+
int dp[61];
23+
bool prime[10000001];
24+
25+
void sieveOfEratosthenes(int n)
26+
{
27+
memset(prime, true, sizeof(prime));
28+
for (int p = 2; p * p <= n; p++)
29+
{
30+
if (prime[p] == true)
31+
{
32+
for (int i = p * p; i <= n; i += p)
33+
prime[i] = false;
34+
}
35+
}
36+
}
37+
38+
int redJohn(int n) {
39+
dp[0] = dp[1] = dp[2] = dp[3] = 1;
40+
for (int i = 4; i <= n; i++)
41+
dp[i] = dp[i - 1] + dp[i - 4];
42+
int count = 0;
43+
for (int i = 2; i <= dp[n]; i++) {
44+
if (prime[i])
45+
count++;
46+
}
47+
return count;
48+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
'''
3+
Copyright (C) 2020, Sathira Silva.
4+
5+
Problem Statement: In this challenge, you will be given an array B and must determine an array A. There is a special rule: For all i,
6+
A[i] <= B[i]. That is, A[i] can be any number you choose such that 1 <= A[i] <= B[i]. Your task is to select a series of A[i] given B[i]
7+
such that the sum of the absolute difference of consecutive pairs of A is maximized. This will be the array's cost, and will be
8+
represented by the variable S below.
9+
10+
S = Sum(|A[i] - A[i - 1]) from i = 2 to N
11+
12+
Approach: To obtain the maximum sum S, there're only two possibilities: A[i] is 1 or B[i]. We don't have to check any number between
13+
1 and B[i]. Therefore, this's sort of a 0-1 Knapsack problem. It can be solved by 4 lines of code using Dynamic Programming.
14+
15+
(01). Subproblem: Optimal sum of the absolute difference of consecutive pairs of prefix A[:i + 1].
16+
max(dp)
17+
Base Case: Initially, dp = [0, 0]. Because When A is empty, maximum sum is 0.
18+
(02). Guessing: Is the maximum sum obtained when A[i] = 1 or A[i] = B[i]?
19+
(03). Recurrence: dp = [max(dp[1] + B[i - 1] - 1, dp[0]), max(dp[1] + abs(B[i] - B[i - 1]), dp[0] + B[i] - 1)]
20+
(04). Topological Order: i from 1 to len(B) - 1
21+
(05). Original Probblem: max(dp) of A[:len(B)]
22+
23+
Time Complexity: O(|B| - 1)
24+
'''
25+
26+
def cost(B):
27+
# Base Case
28+
dp = [0, 0]
29+
for i in range(1,len(B)):
30+
dp = [max(dp[1] + B[i - 1] - 1, dp[0]), max(dp[1] + abs(B[i] - B[i - 1]), dp[0] + B[i] - 1)]
31+
return max(dp)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'''
2+
Copyright (C) 2020, Sathira Silva
3+
4+
Problem Statement: You are working at the cash counter at a fun-fair, and you have different types of coins available to you in
5+
infinite quantities. The value of each coin is already given. Can you determine the number of ways of making change for a particular
6+
number of units using the given types of coins?
7+
8+
Approach: This another variant of 0-1 Knapsack problem. Given different types of coins in infinite amounts, we have to find how many
9+
number of subsets of coins are there such that the sum of the values of the coins in the subset is equal to the given amount. This
10+
can be easily solved by Dynamic Programming.
11+
12+
(01). Subproblem: dp[change]
13+
Number of ways of making change for 'change' units.
14+
(02). Guessing: Include the current coin or not?
15+
(03). Recurrence: dp[change] += dp[change - coin] for all changes that can be made upto n and values of the coins.
16+
dp[change] is dp[change] + number of ways of making change when coin 'coin' is included(for change-coin units).
17+
Base Case: dp[0] - Number of ways of making change for 0 units is 1 i.e. doing nothing.
18+
(04). Topological Order: for coins, the order doesn't matter. for change in coin,...,(n + 1)
19+
(05). Original Problem: dp[n]
20+
Number of ways of making change for n units.
21+
22+
Time Complexity: Less than O(nm); m = number of different coin types
23+
'''
24+
25+
def getWays(n, coins):
26+
dp = [0] * (n + 1)
27+
# Base Case: Number of ways of making change for 0 units is 1 i.e. doing nothing.
28+
dp[0] = 1
29+
for coin in coins:
30+
for change in range(coin, n + 1):
31+
dp[change] += dp[change - coin]
32+
return dp[n]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright (C) 2020, Sathira Silva.
3+
*/
4+
5+
#include <bits/stdc++.h>
6+
7+
using namespace std;
8+
9+
int grundy[501];
10+
11+
int dfs(int u, int p, vector<vector<int>>& G){
12+
for(auto ch : G[u]){
13+
if(ch != p) grundy[u] ^= dfs(ch,u,G);
14+
}
15+
return u == 1 ? grundy[u] : ++grundy[u];
16+
}
17+
18+
19+
string deforestation(int n, vector<vector<int>> tree) {
20+
memset(gr, 0, sizeof(gr));
21+
vector<vector<int>> graph(n+1);
22+
for(auto p : tree){
23+
graph[p[0]].push_back(p[1]);
24+
graph[p[1]].push_back(p[0]);
25+
}
26+
return dfs(1, -1, graph) ? "Alice" : "Bob";
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright (C) 2020, Sathira Silva.
3+
*/
4+
5+
#include <bits/stdc++.h>
6+
7+
using namespace std;
8+
9+
int oddPrime(int num) {
10+
int count = 0;
11+
if (num % 2 == 0)
12+
count++;
13+
while (num % 2 == 0)
14+
num /= 2;
15+
for (int i = 3; i <= sqrt(num); i++) {
16+
while (num % i == 0) {
17+
num /= i;
18+
if (i % 2 != 0) count++;
19+
}
20+
}
21+
if (num > 2)
22+
count++;
23+
return count;
24+
}
25+
26+
int towerBreakers(vector<int> arr) {
27+
int nim = 0;
28+
for (int i = 0; i < arr.size(); i++)
29+
nim ^= oddPrime(arr[i]);
30+
return (nim) ? 1 : 2;
31+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'''
2+
Problem Statement: https://www.hackerrank.com/challenges/tower-breakers-revisited-1/problem
3+
4+
Approach: In this problem, Grundy Number for each pile is the number of prime factors with multiplicity included.
5+
More about Grundy Numbers: https://www.hackerrank.com/topics/game-theory-and-grundy-numbers
6+
http://web.mit.edu/sp.268/www/nim.pdf
7+
'''
8+
9+
#include <bits/stdc++.h>
10+
11+
using namespace std;
12+
13+
int primeFactors(int n) {
14+
int count = 0;
15+
while (n % 2 == 0) {
16+
n = n / 2;
17+
count += 1;
18+
}
19+
for (int i = 3; i <= sqrt(n); i += 2) {
20+
while (n % i == 0) {
21+
n = n / i;
22+
count += 1;
23+
}
24+
}
25+
if (n > 2)
26+
count += 1;
27+
return count;
28+
}
29+
30+
int towerBreakers(vector<int> arr) {
31+
int grundy = 0;
32+
for (int i = 0; i < arr.size(); i++) {
33+
grundy ^= primeFactors(arr[i]);
34+
}
35+
return grundy? 1 : 2;
36+
}

0 commit comments

Comments
 (0)