-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Dynamic Programming Algorithms (#776)
* Add dynamic programming problem implementations and their tests * Fix bugs and improve test cases for dynamic programming algorithms * Fix linters * Modify Dice Throw Test Function Name
- Loading branch information
1 parent
e8cbbce
commit 5ba447e
Showing
24 changed files
with
876 additions
and
0 deletions.
There are no files selected for viewing
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,31 @@ | ||
package dynamic | ||
|
||
import "github.com/TheAlgorithms/Go/math/max" | ||
|
||
// MaxCoins returns the maximum coins we can collect by bursting the balloons | ||
func MaxCoins(nums []int) int { | ||
n := len(nums) | ||
if n == 0 { | ||
return 0 | ||
} | ||
|
||
nums = append([]int{1}, nums...) | ||
nums = append(nums, 1) | ||
|
||
dp := make([][]int, n+2) | ||
for i := range dp { | ||
dp[i] = make([]int, n+2) | ||
} | ||
|
||
for length := 1; length <= n; length++ { | ||
for left := 1; left+length-1 <= n; left++ { | ||
right := left + length - 1 | ||
for k := left; k <= right; k++ { | ||
coins := nums[left-1] * nums[k] * nums[right+1] | ||
dp[left][right] = max.Int(dp[left][right], dp[left][k-1]+dp[k+1][right]+coins) | ||
} | ||
} | ||
} | ||
|
||
return dp[1][n] | ||
} |
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,33 @@ | ||
package dynamic_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/TheAlgorithms/Go/dynamic" | ||
) | ||
|
||
type testCaseBurstBalloons struct { | ||
nums []int | ||
expected int | ||
} | ||
|
||
func getBurstBalloonsTestCases() []testCaseBurstBalloons { | ||
return []testCaseBurstBalloons{ | ||
{[]int{3, 1, 5, 8}, 167}, // Maximum coins from [3,1,5,8] | ||
{[]int{1, 5}, 10}, // Maximum coins from [1,5] | ||
{[]int{1}, 1}, // Single balloon | ||
{[]int{}, 0}, // No balloons | ||
} | ||
|
||
} | ||
|
||
func TestMaxCoins(t *testing.T) { | ||
t.Run("Burst Balloons test cases", func(t *testing.T) { | ||
for _, tc := range getBurstBalloonsTestCases() { | ||
actual := dynamic.MaxCoins(tc.nums) | ||
if actual != tc.expected { | ||
t.Errorf("MaxCoins(%v) = %d; expected %d", tc.nums, actual, tc.expected) | ||
} | ||
} | ||
}) | ||
} |
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,33 @@ | ||
// dicethrow.go | ||
// description: Solves the Dice Throw Problem using dynamic programming | ||
// reference: https://www.geeksforgeeks.org/dice-throw-problem/ | ||
// time complexity: O(m * n) | ||
// space complexity: O(m * n) | ||
|
||
package dynamic | ||
|
||
// DiceThrow returns the number of ways to get sum `sum` using `m` dice with `n` faces | ||
func DiceThrow(m, n, sum int) int { | ||
dp := make([][]int, m+1) | ||
for i := range dp { | ||
dp[i] = make([]int, sum+1) | ||
} | ||
|
||
for i := 1; i <= n; i++ { | ||
if i <= sum { | ||
dp[1][i] = 1 | ||
} | ||
} | ||
|
||
for i := 2; i <= m; i++ { | ||
for j := 1; j <= sum; j++ { | ||
for k := 1; k <= n; k++ { | ||
if j-k >= 0 { | ||
dp[i][j] += dp[i-1][j-k] | ||
} | ||
} | ||
} | ||
} | ||
|
||
return dp[m][sum] | ||
} |
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,42 @@ | ||
package dynamic_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/TheAlgorithms/Go/dynamic" | ||
) | ||
|
||
type testCaseDiceThrow struct { | ||
numDice int | ||
numFaces int | ||
targetSum int | ||
expected int | ||
} | ||
|
||
// getDiceThrowTestCases provides the test cases for DiceThrow | ||
func getDiceThrowTestCases() []testCaseDiceThrow { | ||
return []testCaseDiceThrow{ | ||
{2, 6, 7, 6}, // Two dice, six faces each, sum = 7 | ||
{1, 6, 3, 1}, // One die, six faces, sum = 3 | ||
{3, 4, 5, 6}, // Three dice, four faces each, sum = 5 | ||
{1, 6, 1, 1}, // One die, six faces, sum = 1 | ||
{2, 6, 12, 1}, // Two dice, six faces each, sum = 12 | ||
{3, 6, 18, 1}, // Three dice, six faces each, sum = 18 | ||
{2, 6, 20, 0}, // Two dice, six faces each, sum = 20 (impossible) | ||
{1, 1, 1, 1}, // One die, one face, sum = 1 | ||
{1, 1, 2, 0}, // One die, one face, sum = 2 (impossible) | ||
{2, 1, 2, 1}, // Two dice, one face each, sum = 2 | ||
} | ||
} | ||
|
||
// TestDiceThrow tests the DiceThrow function with basic test cases | ||
func TestDiceThrow(t *testing.T) { | ||
t.Run("Basic test cases", func(t *testing.T) { | ||
for _, tc := range getDiceThrowTestCases() { | ||
actual := dynamic.DiceThrow(tc.numDice, tc.numFaces, tc.targetSum) | ||
if actual != tc.expected { | ||
t.Errorf("DiceThrow(%d, %d, %d) = %d; expected %d", tc.numDice, tc.numFaces, tc.targetSum, actual, tc.expected) | ||
} | ||
} | ||
}) | ||
} |
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,47 @@ | ||
package dynamic | ||
|
||
import ( | ||
"github.com/TheAlgorithms/Go/math/max" | ||
"github.com/TheAlgorithms/Go/math/min" | ||
) | ||
|
||
// EggDropping finds the minimum number of attempts needed to find the critical floor | ||
// with `eggs` number of eggs and `floors` number of floors | ||
func EggDropping(eggs, floors int) int { | ||
// Edge case: If there are no floors, no attempts needed | ||
if floors == 0 { | ||
return 0 | ||
} | ||
// Edge case: If there is one floor, one attempt needed | ||
if floors == 1 { | ||
return 1 | ||
} | ||
// Edge case: If there is one egg, need to test all floors one by one | ||
if eggs == 1 { | ||
return floors | ||
} | ||
|
||
// Initialize DP table | ||
dp := make([][]int, eggs+1) | ||
for i := range dp { | ||
dp[i] = make([]int, floors+1) | ||
} | ||
|
||
// Fill the DP table for 1 egg | ||
for j := 1; j <= floors; j++ { | ||
dp[1][j] = j | ||
} | ||
|
||
// Fill the DP table for more than 1 egg | ||
for i := 2; i <= eggs; i++ { | ||
for j := 2; j <= floors; j++ { | ||
dp[i][j] = int(^uint(0) >> 1) // initialize with a large number | ||
for x := 1; x <= j; x++ { | ||
// Recurrence relation to fill the DP table | ||
res := max.Int(dp[i-1][x-1], dp[i][j-x]) + 1 | ||
dp[i][j] = min.Int(dp[i][j], res) | ||
} | ||
} | ||
} | ||
return dp[eggs][floors] | ||
} |
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,35 @@ | ||
package dynamic_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/TheAlgorithms/Go/dynamic" | ||
) | ||
|
||
type testCaseEggDropping struct { | ||
eggs int | ||
floors int | ||
expected int | ||
} | ||
|
||
func getEggDroppingTestCases() []testCaseEggDropping { | ||
return []testCaseEggDropping{ | ||
{1, 10, 10}, // One egg, need to test all floors | ||
{2, 10, 4}, // Two eggs and ten floors | ||
{3, 14, 4}, // Three eggs and fourteen floors | ||
{2, 36, 8}, // Two eggs and thirty-six floors | ||
{2, 0, 0}, // Two eggs, zero floors | ||
} | ||
|
||
} | ||
|
||
func TestEggDropping(t *testing.T) { | ||
t.Run("Egg Dropping test cases", func(t *testing.T) { | ||
for _, tc := range getEggDroppingTestCases() { | ||
actual := dynamic.EggDropping(tc.eggs, tc.floors) | ||
if actual != tc.expected { | ||
t.Errorf("EggDropping(%d, %d) = %d; expected %d", tc.eggs, tc.floors, actual, tc.expected) | ||
} | ||
} | ||
}) | ||
} |
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,36 @@ | ||
// interleavingstrings.go | ||
// description: Solves the Interleaving Strings problem using dynamic programming | ||
// reference: https://en.wikipedia.org/wiki/Interleaving_strings | ||
// time complexity: O(m*n) | ||
// space complexity: O(m*n) | ||
|
||
package dynamic | ||
|
||
// IsInterleave checks if string `s1` and `s2` can be interleaved to form string `s3` | ||
func IsInterleave(s1, s2, s3 string) bool { | ||
if len(s1)+len(s2) != len(s3) { | ||
return false | ||
} | ||
|
||
dp := make([][]bool, len(s1)+1) | ||
for i := range dp { | ||
dp[i] = make([]bool, len(s2)+1) | ||
} | ||
|
||
dp[0][0] = true | ||
for i := 1; i <= len(s1); i++ { | ||
dp[i][0] = dp[i-1][0] && s1[i-1] == s3[i-1] | ||
} | ||
|
||
for j := 1; j <= len(s2); j++ { | ||
dp[0][j] = dp[0][j-1] && s2[j-1] == s3[j-1] | ||
} | ||
|
||
for i := 1; i <= len(s1); i++ { | ||
for j := 1; j <= len(s2); j++ { | ||
dp[i][j] = (dp[i-1][j] && s1[i-1] == s3[i+j-1]) || (dp[i][j-1] && s2[j-1] == s3[i+j-1]) | ||
} | ||
} | ||
|
||
return dp[len(s1)][len(s2)] | ||
} |
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,38 @@ | ||
package dynamic_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/TheAlgorithms/Go/dynamic" | ||
) | ||
|
||
type testCaseInterleaving struct { | ||
s1, s2, s3 string | ||
expected bool | ||
} | ||
|
||
func getInterleavingTestCases() []testCaseInterleaving { | ||
return []testCaseInterleaving{ | ||
{"aab", "axy", "aaxaby", true}, // Valid interleaving | ||
{"aab", "axy", "abaaxy", false}, // Invalid interleaving | ||
{"", "", "", true}, // All empty strings | ||
{"abc", "", "abc", true}, // Only s1 matches s3 | ||
{"", "xyz", "xyz", true}, // Only s2 matches s3 | ||
{"abc", "xyz", "abxcyz", true}, // Valid interleaving | ||
{"aaa", "aaa", "aaaaaa", true}, // Identical strings | ||
{"aaa", "aaa", "aaaaaaa", false}, // Extra character | ||
{"abc", "def", "abcdef", true}, // Concatenation order | ||
{"abc", "def", "adbcef", true}, // Valid mixed interleaving | ||
} | ||
} | ||
|
||
func TestIsInterleave(t *testing.T) { | ||
t.Run("Interleaving Strings test cases", func(t *testing.T) { | ||
for _, tc := range getInterleavingTestCases() { | ||
actual := dynamic.IsInterleave(tc.s1, tc.s2, tc.s3) | ||
if actual != tc.expected { | ||
t.Errorf("IsInterleave(%q, %q, %q) = %v; expected %v", tc.s1, tc.s2, tc.s3, actual, tc.expected) | ||
} | ||
} | ||
}) | ||
} |
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,34 @@ | ||
// longestarithmeticsubsequence.go | ||
// description: Implementation of the Longest Arithmetic Subsequence problem | ||
// reference: https://en.wikipedia.org/wiki/Longest_arithmetic_progression | ||
// time complexity: O(n^2) | ||
// space complexity: O(n^2) | ||
|
||
package dynamic | ||
|
||
// LongestArithmeticSubsequence returns the length of the longest arithmetic subsequence | ||
func LongestArithmeticSubsequence(nums []int) int { | ||
n := len(nums) | ||
if n <= 1 { | ||
return n | ||
} | ||
|
||
dp := make([]map[int]int, n) | ||
for i := range dp { | ||
dp[i] = make(map[int]int) | ||
} | ||
|
||
maxLength := 1 | ||
|
||
for i := 1; i < n; i++ { | ||
for j := 0; j < i; j++ { | ||
diff := nums[i] - nums[j] | ||
dp[i][diff] = dp[j][diff] + 1 | ||
if dp[i][diff]+1 > maxLength { | ||
maxLength = dp[i][diff] + 1 | ||
} | ||
} | ||
} | ||
|
||
return maxLength | ||
} |
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,38 @@ | ||
package dynamic_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/TheAlgorithms/Go/dynamic" | ||
) | ||
|
||
type testCaseLongestArithmeticSubsequence struct { | ||
nums []int | ||
expected int | ||
} | ||
|
||
func getLongestArithmeticSubsequenceTestCases() []testCaseLongestArithmeticSubsequence { | ||
return []testCaseLongestArithmeticSubsequence{ | ||
{[]int{3, 6, 9, 12}, 4}, // Arithmetic sequence of length 4 | ||
{[]int{9, 4, 7, 2, 10}, 3}, // Arithmetic sequence of length 3 | ||
{[]int{20, 1, 15, 3, 10, 5, 8}, 4}, // Arithmetic sequence of length 4 | ||
{[]int{1, 2, 3, 4, 5}, 5}, // Arithmetic sequence of length 5 | ||
{[]int{10, 7, 4, 1}, 4}, // Arithmetic sequence of length 4 | ||
{[]int{1, 5, 7, 8, 5, 3, 4, 3, 1, 2}, 4}, // Arithmetic sequence of length 4 | ||
{[]int{1, 3, 5, 7, 9}, 5}, // Arithmetic sequence of length 5 | ||
{[]int{5, 10, 15, 20}, 4}, // Arithmetic sequence of length 4 | ||
{[]int{1}, 1}, // Single element, length is 1 | ||
{[]int{}, 0}, // Empty array, length is 0 | ||
} | ||
} | ||
|
||
func TestLongestArithmeticSubsequence(t *testing.T) { | ||
t.Run("Longest Arithmetic Subsequence test cases", func(t *testing.T) { | ||
for _, tc := range getLongestArithmeticSubsequenceTestCases() { | ||
actual := dynamic.LongestArithmeticSubsequence(tc.nums) | ||
if actual != tc.expected { | ||
t.Errorf("LongestArithmeticSubsequence(%v) = %v; expected %v", tc.nums, actual, tc.expected) | ||
} | ||
} | ||
}) | ||
} |
Oops, something went wrong.