This is basically follow-up to previous post where subset sum problem without repetition has been discussed.
1. What are the minimum no. of coins required to get to the given target? (nothing but one of the variations of subset sum problem with repetition)
Say, f[i, sum] finds the minimum no. of coins required to get to 'sum' by using given coins up-to ith index
f[i, w] = 0 if i<= 0
0 if sum < 0
1 if sum == weights[i]
f[i-1, w] if weights[i] > w
f[i-1, w] if f(i, w-weights[i]) == 0
f(i, w-weighs[i]) + 1 if f[i-1, w] == 0
min (f(i-1, w), f(i, w-weighs[i]) + 1)
Below is the corresponding code...
f[i, sum] = 0 if i<= 0
0 if sum < 0
1 if sum == 0
f[i-1, sum] + f[i, sum-weights[i]]
Another way to define the same is by,
f[i, sum] = 0 if i <=0
f[i-1, sum] + 1 if weights[i] == sum
f[i-1, sum] if weights[i] > sum
f[i-1, sum] + f[i, sum-weights[i]]
Below is the corresponding code...
False if sum < 0
True if sum == 0
f[i-1, sum] || [i, sum-weights[i]]
Or, f[i, sum] = False if i<= 0
True if weights[i] == sum
f[i-1, w] if weights[i] > sum
f[i-1, w] || f[i, w- weights[i]]
Below is the corresponding code...
1. What are the minimum no. of coins required to get to the given target? (nothing but one of the variations of subset sum problem with repetition)
Say, f[i, sum] finds the minimum no. of coins required to get to 'sum' by using given coins up-to ith index
f[i, w] = 0 if i<= 0
0 if sum < 0
1 if sum == weights[i]
f[i-1, w] if weights[i] > w
f[i-1, w] if f(i, w-weights[i]) == 0
f(i, w-weighs[i]) + 1 if f[i-1, w] == 0
min (f(i-1, w), f(i, w-weighs[i]) + 1)
Below is the corresponding code...
Code Snippet
- public int DP_CoinChange_GetMinimalDemoninations(int[] coins, int sum)
- {
- coins.ThrowIfNull("coins");
- coins.Throw("coins", c => c.Length == 0 || c.Any(ci => ci <= 0));
- sum.Throw("sum", s => s <= 0);
- int[][] DP_Cache = new int[coins.Length + 1][];
- for (int i = 0; i <= coins.Length; i++)
- {
- DP_Cache[i] = new int[sum + 1];
- }
- for(int i = 1;i<=coins.Length;i++)
- {
- for(int s=0;s<=sum;s++)
- {
- if (coins[i - 1] == s)
- {
- //k, we can get to sum using just the current coin
- //so, assign to 1, no need to process further
- DP_Cache[i][s] = 1;
- }
- else
- {
- //initialize the value withouth the current value
- int minNoOfCounsWithoutUsingCurrentCoin_I = DP_Cache[i - 1][s];
- DP_Cache[i][s] = minNoOfCounsWithoutUsingCurrentCoin_I;
- if ((s > coins[i - 1]) //current coin can particiapte
- && (DP_Cache[i][s - coins[i - 1]] != 0))
- {
- int noOfCoinsUsedIncludingCurrentCoin_I =
- DP_Cache[i][s - coins[i - 1]] + 1;
- if (minNoOfCounsWithoutUsingCurrentCoin_I == 0)
- {
- //so far we couldnt identify coins that sums to 's'
- DP_Cache[i][s] = noOfCoinsUsedIncludingCurrentCoin_I;
- }
- else
- {
- int min = this.Min(noOfCoinsUsedIncludingCurrentCoin_I,
- minNoOfCounsWithoutUsingCurrentCoin_I);
- DP_Cache[i][s] = min;
- }
- }
- }
- }
- }
- return DP_Cache[coins.Length][sum];
- }
Total number of ways to make the change
Say, f[i, sum] represents total number of ways to make the change by using upto 'i' coins (coins can be used repeatedly) then,f[i, sum] = 0 if i<= 0
0 if sum < 0
1 if sum == 0
f[i-1, sum] + f[i, sum-weights[i]]
Another way to define the same is by,
f[i, sum] = 0 if i <=0
f[i-1, sum] + 1 if weights[i] == sum
f[i-1, sum] if weights[i] > sum
f[i-1, sum] + f[i, sum-weights[i]]
Below is the corresponding code...
Code Snippet
- public int DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(int[] a, int sum)
- {
- a.ThrowIfNull("a");
- int[][] DP_Memoization = new int[a.Length][];
- for (int i = 0; i < a.Length; i++)
- {
- DP_Memoization[i] = new int[sum + 1];
- for (int s = 0; s <= sum; s++)
- {
- DP_Memoization[i][s] = -1;
- }
- }
- var r = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, a.Length-1, sum, DP_Memoization);
- return r;
- }
- private int DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(int[] a, int i, int sum, int[][] DP_Memoization)
- {
- if(sum <= 0)
- {
- return 0;
- }
- if(i < 0)
- {
- return 0;
- }
- if(DP_Memoization[i][sum] != -1)
- {
- return DP_Memoization[i][sum];
- }
- int noOfWays = 0;
- //finding the number of ways without including current item
- noOfWays += this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, i - 1, sum, DP_Memoization);
- if(a[i] == sum)
- {
- noOfWays++;
- }
- if(sum > a[i])
- {
- noOfWays += this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, i, sum - a[i], DP_Memoization);
- }
- DP_Memoization[i][sum] = noOfWays;
- return noOfWays;
- }
Decision making - finding whether its possible to get the coin change
f[i, sum] = False if i<=0False if sum < 0
True if sum == 0
f[i-1, sum] || [i, sum-weights[i]]
Or, f[i, sum] = False if i<= 0
True if weights[i] == sum
f[i-1, w] if weights[i] > sum
f[i-1, w] || f[i, w- weights[i]]
Below is the corresponding code...
Code Snippet
- public bool DP_CoinChange_SubsetSumWithRep(int[] coins, int sum)
- {
- coins.ThrowIfNull("coins");
- coins.Throw("coins", c => c.Length == 0 || c.Any(ci => ci <= 0));
- sum.Throw("sum", s => s <= 0);
- bool[][] DP_Cache = new bool[coins.Length+1][];
- for(int i=0;i<=coins.Length;i++)
- {
- DP_Cache[i] = new bool[sum + 1];
- }
- for(int i =1;i<=coins.Length;i++)
- {
- for(int s=0;s<=sum;s++)
- {
- DP_Cache[i][s] = (coins[i-1] == s)
- || DP_Cache[i - 1][s];
- if(!DP_Cache[i][s]
- && (s > coins[i-1]))
- {
- DP_Cache[i][s] = DP_Cache[i][s - coins[i-1]];
- }
- }
- }
- return DP_Cache[coins.Length][sum];
- }
Code Snippet
- public bool DoesSubsetSumExistWithRepetition(int[] a, int sum)
- {
- a.ThrowIfNull("a");
- bool f = this.DoesSubsetSumExistWithRepetition(a, a.Length - 1, sum);
- return f;
- }
- public bool DoesSubsetSumExistWithRepetition(int[] a, int index, int sum)
- {
- if(index < 0)
- {
- return false;
- }
- if(a[index] == sum)
- {
- return true;
- }
- bool f = this.DoesSubsetSumExistWithRepetition(a, index - 1, sum);
- if(f)
- {
- return f;
- }
- if(sum > a[index])
- {
- f = this.DoesSubsetSumExistWithRepetition(a, index, sum - a[index]);
- return f;
- }
- return false;
- }
Getting unique subsets that sums up-to given sum
Code Snippet
- public int[][] GetUniqueSubsetSumPathsWithRepetition(int[] a, int sum)
- {
- a.ThrowIfNull("a");
- var r = this.GetUniqueSubsetSumPathsWithRepetition(a, a.Length - 1, sum);
- return r;
- }
- private int[][] GetUniqueSubsetSumPathsWithRepetition(int[] a, int index, int sum)
- {
- if (index < 0)
- {
- return new int[0][];
- }
- List<int[]> paths = new List<int[]>();
- if (a[index] == sum)
- {
- paths.Add(new int[1] { index });
- }
- var p1 = this.GetUniqueSubsetSumPathsWithRepetition(a, index - 1, sum);
- paths.AddRange(p1);
- if (sum > a[index])
- {
- var p2 = this.GetUniqueSubsetSumPathsWithRepetition(a, index, sum - a[index]);
- paths.AddRange(this.MergePaths(p2, index));
- }
- return paths.ToArray();
- }
- private int[][] MergePaths(int[][] paths,
- int index)
- {
- Assert.IsNotNull(paths);
- int[][] mergedPaths = new int[paths.Length][];
- Assert.IsTrue(paths.All(p => p.Length > 0));
- for (int i = 0; i < paths.Length; i++)
- {
- List<int> mergedPath = new List<int>();
- mergedPath.AddRange(paths[i]);
- mergedPath.Add(index);
- mergedPaths[i] = mergedPath.ToArray();
- }
- return mergedPaths;
- }
Unit Tests
Code Snippet
- [TestCategory(Constants.DynamicProgramming)]
- [TestMethod]
- public void SubsetSumWithRepetition_CoinChange_Tests()
- {
- int[] a = new int[] { 2, 3, 6, 7 };
- bool f = this.DoesSubsetSumExistWithRepetition(a, 7);
- Assert.IsTrue(f);
- f = this.DP_CoinChange_SubsetSumWithRep(a, 7);
- Assert.IsTrue(f);
- var p = this.GetUniqueSubsetSumPathsWithRepetition(a, 7);
- Assert.AreEqual(2, p.Length);
- int noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 7);
- Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
- int min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
- Assert.AreEqual(1, min);
- a = new int[] { 1, 2, 5 };
- f = this.DoesSubsetSumExistWithRepetition(a, 7);
- Assert.IsTrue(f);
- f = this.DP_CoinChange_SubsetSumWithRep(a, 7);
- Assert.IsTrue(f);
- p = this.GetUniqueSubsetSumPathsWithRepetition(a, 7);
- Assert.AreEqual(6, p.Length);
- noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 7);
- Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
- Assert.AreEqual(2, min);
- a = new int[] { 1, 2 };
- f = this.DoesSubsetSumExistWithRepetition(a, 6);
- Assert.IsTrue(f);
- f = this.DP_CoinChange_SubsetSumWithRep(a, 6);
- Assert.IsTrue(f);
- p = this.GetUniqueSubsetSumPathsWithRepetition(a, 6);
- Assert.AreEqual(4, p.Length);
- noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 6);
- Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
- a = new int[] { 4, 3, 2, 2, 1, 1 };
- a = a.Distinct().ToArray();
- f = this.DoesSubsetSumExistWithRepetition(a, 4);
- Assert.IsTrue(f);
- f = this.DP_CoinChange_SubsetSumWithRep(a, 4);
- Assert.IsTrue(f);
- p = this.GetUniqueSubsetSumPathsWithRepetition(a, 4);
- Assert.AreEqual(5, p.Length);
- noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 4);
- Assert.AreEqual(p.Length, noOfWaysToGetCoinChange);
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 4);
- Assert.AreEqual(1, min);
- a = new int[] { 11, 12, 13 };
- f = this.DoesSubsetSumExistWithRepetition(a, 4);
- Assert.IsFalse(f);
- f = this.DP_CoinChange_SubsetSumWithRep(a, 4);
- Assert.IsFalse(f);
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 7);
- Assert.AreEqual(0, min);
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 13);
- Assert.AreEqual(1, min);
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 37);
- Assert.AreEqual(3, min);
- a = new int[] { 4, 3, 1 };
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 6);
- Assert.AreEqual(2, min);
- a = new int[] { 1, 2, 3 };
- min = this.DP_CoinChange_GetMinimalDemoninations(a, 4);
- Assert.AreEqual(2, min);
- a = new int[] { 1, 2, 4 };
- noOfWaysToGetCoinChange = this.DP_Memoization_GetNumberOfWaysToMakeTheChange_SubsetSumWithRepetition(a, 5);
- Assert.AreEqual(4, noOfWaysToGetCoinChange);
- }
No comments:
Post a Comment