Skip to content

Commit 85a3e3e

Browse files
committed
Added dynamic programming
1 parent 90c66b9 commit 85a3e3e

File tree

11 files changed

+1401
-0
lines changed

11 files changed

+1401
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* @file CoinChange.h
3+
* @author (original JAVA) William Fiset, [email protected]
4+
* (conversion to C++) Armin Zare Zadeh, [email protected]
5+
* @date 15 July 2020
6+
* @version 0.1
7+
* @brief The coin change problem is an unbounded knapsack problem variant. The problem asks you to find
8+
* the minimum number of coins required for a certain amount of change given the coin denominations.
9+
* You may use each coin denomination as many times as you please.
10+
*
11+
* <p>Tested against: https://leetcode.com/problems/coin-change/
12+
*/
13+
14+
#ifndef D_COINCHANGE_H
15+
#define D_COINCHANGE_H
16+
17+
#include <vector>
18+
#include <deque>
19+
#include <list>
20+
#include <set> // set and multiset
21+
#include <map> // map and multimap
22+
#include <unordered_set> // unordered set/multiset
23+
#include <unordered_map> // unordered map/multimap
24+
#include <iterator>
25+
#include <algorithm>
26+
#include <numeric> // some numeric algorithm
27+
#include <functional>
28+
#include <stack>
29+
30+
#include <sstream>
31+
#include <memory>
32+
#include <iostream>
33+
#include <cmath>
34+
35+
namespace dsa {
36+
37+
class CoinChange {
38+
39+
private:
40+
static const int INF;
41+
public:
42+
static int coinChange(const std::vector<int>& coins, int amount) {
43+
44+
if (coins.size() == 0) throw std::invalid_argument("No coin values :/");
45+
46+
int N = coins.size();
47+
// Initialize table and set first row to be infinity
48+
std::vector<std::vector<int>> DP(N + 1, std::vector<int>(amount + 1, INF));
49+
DP[1][0] = 0;
50+
51+
// Iterate through all the coins
52+
for (int i = 1; i <= N; i++) {
53+
54+
int coinValue = coins[i - 1];
55+
for (int j = 1; j <= amount; j++) {
56+
57+
// Consider not selecting this coin
58+
DP[i][j] = DP[i - 1][j];
59+
60+
// Try selecting this coin if it's better
61+
if (j - coinValue >= 0 && DP[i][j - coinValue] + 1 < DP[i][j])
62+
DP[i][j] = DP[i][j - coinValue] + 1;
63+
}
64+
}
65+
66+
// The amount we wanted to make cannot be made :/
67+
if (DP[N][amount] == INF) return -1;
68+
69+
// Return the minimum number of coins needed
70+
return DP[N][amount];
71+
}
72+
73+
74+
static int coinChangeSpaceEfficient(const std::vector<int>& coins, int amount) {
75+
76+
if (coins.size() == 0) throw std::invalid_argument("Coins array is null");
77+
78+
// Initialize table and set everything to infinity except first cell
79+
std::vector<int> DP(amount + 1, INF);
80+
DP[0] = 0;
81+
82+
for (int i = 1; i <= amount; i++)
83+
for (int coinValue : coins)
84+
if (i - coinValue >= 0 && DP[i - coinValue] + 1 < DP[i]) DP[i] = DP[i - coinValue] + 1;
85+
86+
// The amount we wanted to make cannot be made :/
87+
if (DP[amount] == INF) return -1;
88+
89+
// Return the minimum number of coins needed
90+
return DP[amount];
91+
}
92+
93+
public:
94+
// The recursive approach has the advantage that it does not have to visit
95+
// all possible states like the tabular approach does. This can speedup
96+
// things especially if the coin denominations are large.
97+
static int coinChangeRecursive(const std::vector<int>& coins, int amount) {
98+
99+
if (coins.size() == 0) throw std::invalid_argument("Coins array is null");
100+
if (amount < 0) return -1;
101+
102+
std::vector<int> DP(amount + 1);
103+
return coinChangeRecursive(amount, coins, DP);
104+
}
105+
106+
private:
107+
// Private helper method to actually go the recursion
108+
static int coinChangeRecursive(int amount, const std::vector<int>& coins, std::vector<int>& DP) {
109+
110+
// Base cases.
111+
if (amount < 0) return -1;
112+
if (amount == 0) return 0;
113+
if (DP[amount] != 0) return DP[amount];
114+
115+
int minCoins = INF;
116+
for (int coinValue : coins) {
117+
118+
int newAmount = amount - coinValue;
119+
int value = coinChangeRecursive(newAmount, coins, DP);
120+
if (value != -1 && value < minCoins) minCoins = value + 1;
121+
}
122+
123+
// If we weren't able to find some coins to make our
124+
// amount then cache -1 as the answer.
125+
return DP[amount] = (minCoins == INF) ? -1 : minCoins;
126+
}
127+
128+
};
129+
const int CoinChange::INF = 987654321;
130+
131+
132+
void CoinChange_test()
133+
{
134+
const std::vector<int> coins{2, 6, 1};
135+
std::cout << CoinChange::coinChange(coins, 17) << std::endl;
136+
std::cout << CoinChange::coinChangeSpaceEfficient(coins, 17) << std::endl;
137+
std::cout << CoinChange::coinChangeRecursive(coins, 17) << std::endl;
138+
}
139+
140+
} // namespace dsa
141+
142+
#endif /* D_JOSEPHUSPROBLEM_H */
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* @file JosephusProblem.h
3+
* @author (original JAVA) Micah Stairs
4+
* (conversion to C++) Armin Zare Zadeh, [email protected]
5+
* @date 15 July 2020
6+
* @version 0.1
7+
* @brief An implementation of the Josephus problem Time complexity: O(n)
8+
*/
9+
10+
#ifndef D_JOSEPHUSPROBLEM_H
11+
#define D_JOSEPHUSPROBLEM_H
12+
13+
#include <vector>
14+
#include <deque>
15+
#include <list>
16+
#include <set> // set and multiset
17+
#include <map> // map and multimap
18+
#include <unordered_set> // unordered set/multiset
19+
#include <unordered_map> // unordered map/multimap
20+
#include <iterator>
21+
#include <algorithm>
22+
#include <numeric> // some numeric algorithm
23+
#include <functional>
24+
#include <stack>
25+
26+
#include <sstream>
27+
#include <memory>
28+
#include <iostream>
29+
#include <cmath>
30+
31+
namespace dsa {
32+
33+
class JosephusProblem {
34+
public:
35+
// Suppose there are n people in a circle and person
36+
// 0 kill the k'th person, then the k'th person kills
37+
// the 2k'th person and so on until only one person remains.
38+
// The question is who lives?
39+
// Let n be the number of people and k the hop size
40+
static int josephus(int n, int k) {
41+
std::vector<int> dp(n, 0);
42+
for (int i = 1; i < n; i++) dp[i] = (dp[i - 1] + k) % (i + 1);
43+
return dp[n - 1];
44+
}
45+
46+
};
47+
48+
49+
void Josephusproblem_test()
50+
{
51+
int n = 41, k = 2;
52+
std::cout << JosephusProblem::josephus(n, k) << std::endl;
53+
54+
n = 25;
55+
k = 18;
56+
std::cout << JosephusProblem::josephus(n, k) << std::endl;
57+
58+
n = 5;
59+
k = 2;
60+
std::cout << JosephusProblem::josephus(n, k) << std::endl;
61+
}
62+
63+
} // namespace dsa
64+
65+
#endif /* D_JOSEPHUSPROBLEM_H */
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* @file KnapsackUnbounded.h
3+
* @author (original JAVA) William Fiset, [email protected]
4+
* (conversion to C++) Armin Zare Zadeh, [email protected]
5+
* @date 15 July 2020
6+
* @version 0.1
7+
* @brief This file contains a dynamic programming solutions to the classic unbounded knapsack problem
8+
* where are you are trying to maximize the total profit of items selected without exceeding the
9+
* capacity of your knapsack.
10+
*
11+
* <p>Version 1: Time Complexity: O(nW) Version 1 Space Complexity: O(nW)
12+
*
13+
* <p>Version 2: Time Complexity: O(nW) Space Complexity: O(W)
14+
*
15+
* <p>Tested code against: https://www.hackerrank.com/challenges/unbounded-knapsack
16+
*/
17+
18+
#ifndef D_KNAPSACKUNBOUNDED_H
19+
#define D_KNAPSACKUNBOUNDED_H
20+
21+
#include <vector>
22+
#include <deque>
23+
#include <list>
24+
#include <set> // set and multiset
25+
#include <map> // map and multimap
26+
#include <unordered_set> // unordered set/multiset
27+
#include <unordered_map> // unordered map/multimap
28+
#include <iterator>
29+
#include <algorithm>
30+
#include <numeric> // some numeric algorithm
31+
#include <functional>
32+
#include <stack>
33+
34+
#include <sstream>
35+
#include <memory>
36+
#include <iostream>
37+
#include <cmath>
38+
39+
namespace dsa {
40+
41+
class KnapsackUnbounded {
42+
public:
43+
/**
44+
* @param maxWeight - The maximum weight of the knapsack
45+
* @param W - The weights of the items
46+
* @param V - The values of the items
47+
* @return The maximum achievable profit of selecting a subset of the elements such that the
48+
* capacity of the knapsack is not exceeded
49+
*/
50+
static int unboundedKnapsack(int maxWeight, const std::vector<int>& W, const std::vector<int>& V) {
51+
52+
if (W.size() == 0 || W.size() != V.size() || maxWeight < 0)
53+
throw std::invalid_argument("Invalid input");
54+
55+
int N = W.size();
56+
57+
// Initialize a table where individual rows represent items
58+
// and columns represent the weight of the knapsack
59+
std::vector<std::vector<int>> DP(N + 1, std::vector<int>(maxWeight + 1, 0));
60+
61+
// Loop through items
62+
for (int i = 1; i <= N; i++) {
63+
64+
// Get the value and weight of the item
65+
int w = W[i - 1], v = V[i - 1];
66+
67+
// Consider all possible knapsack sizes
68+
for (int sz = 1; sz <= maxWeight; sz++) {
69+
70+
// Try including the current element
71+
if (sz >= w) DP[i][sz] = DP[i][sz - w] + v;
72+
73+
// Check if not selecting this item at all is more profitable
74+
if (DP[i - 1][sz] > DP[i][sz]) DP[i][sz] = DP[i - 1][sz];
75+
}
76+
}
77+
78+
// Return the best value achievable
79+
return DP[N][maxWeight];
80+
}
81+
82+
83+
public:
84+
static int unboundedKnapsackSpaceEfficient(int maxWeight, const std::vector<int>& W, const std::vector<int>& V) {
85+
86+
if (W.size() == 0 || W.size() != V.size() || maxWeight < 0)
87+
throw std::invalid_argument("Invalid input");
88+
89+
int N = W.size();
90+
91+
// Initialize a table where we will only keep track of
92+
// the best possible value for each knapsack weight
93+
std::vector<int> DP(maxWeight + 1, 0);
94+
95+
// Consider all possible knapsack sizes
96+
for (int sz = 1; sz <= maxWeight; sz++) {
97+
98+
// Loop through items
99+
for (int i = 0; i < N; i++) {
100+
101+
// First check that we can include this item (we can't include it if
102+
// it's too heavy for our knapsack). Assumming it fits inside the
103+
// knapsack check if including this element would be profitable.
104+
if (sz - W[i] >= 0 && DP[sz - W[i]] + V[i] > DP[sz]) DP[sz] = DP[sz - W[i]] + V[i];
105+
}
106+
}
107+
108+
// Return the best value achievable
109+
return DP[maxWeight];
110+
}
111+
112+
};
113+
114+
115+
void UnboundedKnapsack_test()
116+
{
117+
std::vector<int> W{3, 6, 2};
118+
std::vector<int> V{5, 20, 3};
119+
int knapsackValue = KnapsackUnbounded::unboundedKnapsackSpaceEfficient(10, W, V);
120+
std::cout << "Maximum knapsack value: " << knapsackValue << std::endl;
121+
}
122+
123+
} // namespace dsa
124+
125+
#endif /* D_KNAPSACKUNBOUNDED_H */

0 commit comments

Comments
 (0)