Understanding the recursive knapsack - c++

When I learned recursion, I understood it with 3 steps, where one of them was the assumption step. For example, if I want to find the sum of n integers:
int func(int n)
{
if(n <= 1)
return n;
int ans = func(n-1); //Assumption step
return ans + n;
}
When n = 5, then In the assumption step I assume that func(n-1) ie; func(4) will give me the correct answer 10. So keeping that assumption in mind, I just write return ans + n; Since I know ans has 10 in it and then just add the current n to it, which will give me my answer of 15.
Now when I look at the knapsack code:
int knapsack(int wt[], int val[], int capacity, int n)
{
if (n == 0 || capacity == 0)
return 0;
if(wt[n-1] > capacity)
return knapsack(wt, val, capacity-wt[n-1], n-1);
return max(val[n-1] + knapsack(wt, val, capacity-wt[n-1], n-1), knapsack(wt, val, capacity, n-1));
}
int main()
{
int wt[] = {1, 3, 4, 5};
int val[] = {1, 4, 5, 7};
int capacity = 7;
cout<<knapsack(wt,val,capacity, 4); // gives 9
}
Which function do I trust to give me the solution for the smaller value n-1 and what will it be. How can I apply and understand the assumption step in the knapsack code?

Related

Count how many iterations of deletion until array is ordered

I'm trying to write a program whose input is an array of integers, and its size. This code has to delete each element which is smaller than the element to the left. We want to find number of times that we can process the array this way, until we can no longer delete any more elements.
The contents of the array after we return are unimportant - only the return value is of interest.
For example: given the array [10, 9, 7, 8, 6, 5, 3, 4, 2, 1], the function should return 2, because:
[10,9,7,8,6,5,3,4,2,1] ā†’ [10,8,4] ā†’ [10]
For example: given the array [1,2,3,4], the function should return 0, because
No element is larger than the element to its right
I want each element to remove the right element if it is more than its right element. We get a smaller array. Then we repeat this operation again. Until we get to an array in which no element can delete another element. I want to calculate the number of steps performed.
int Mafia(int n, vector <int> input_array)
{
int ptr = n;
int last_ptr = n;
int night_Count = 0;
do
{
last_ptr = ptr;
ptr = 1;
for (int i = 1; i < last_ptr; i++)
{
if (input_array[i] >= input_array[i - 1])
{
input_array[ptr++] = input_array[i];
}
}
night_Count++;
} while (last_ptr > ptr);
return night_Count - 1;
}
My code works but I want it to be faster.
Do you have any idea to make this code faster, or another way that is faster than this?
Here is a O(NlogN) solution.
The idea is to iterate over the array and keep tracking candidateKillers which could kill unvisited numbers. Then we find the killer for the current number by using binary search and update the maximum iterations if needed.
Since we iterate over the array which has N numbers and apply log(N) binary search for each number, the overall time complexity is O(NlogN).
Alogrithm
If the current number is greater or equal than the number before it, it could be a killer for numbers after it.
For each killer, we keep tracking its index idx, the number of it num and the iterations needed to reach that killer iters.
The numbers in the candidateKillers by its nature are non-increasing (see next point). Therefore we can apply binary search to find the killer of the current number, which is the one that is a) the closest to the current number b) greater than the current number. This is implemented in searchKiller.
If the current number will be killed by a number in candidateKillers with killerPos, then all candidate killers after killerPos are outdated, because those outdated killers will be killed before the numbers after the current number reach them. If the current number is greater than all candidateKillers, then all the candidateKillers can be discarded.
When we find the killer of the current number, we increase the iters of the killer by one. Because from now on, one more iteration is needed to reach that killer where the current number need to be killed first.
class Solution {
public:
int countIterations(vector<int>& array) {
if (array.size() <= 1) {
return 0;
}
int ans = 0;
vector<Killer> candidateKillers = {Killer(0, array[0], 1)};
for (auto i = 1; i < array.size(); i++) {
int curNum = array[i];
int killerPos = searchKiller(candidateKillers, curNum);
if (killerPos == -1) {
// current one is the largest so far and all candidateKillers before are outdated
candidateKillers = {Killer(i, curNum, 1)};
continue;
}
// get rid of outdated killers
int popCount = candidateKillers.size() - 1 - killerPos;
for (auto j = 0; j < popCount; j++) {
candidateKillers.pop_back();
}
Killer killer = candidateKillers[killerPos];
ans = max(killer.iters, ans);
if (curNum < array[i-1]) {
// since the killer of the current one may not even be in the list e.g., if current is 4 in [6,5,4]
if (killer.idx == i - 1) {
candidateKillers[killerPos].iters += 1;
}
} else {
candidateKillers[killerPos].iters += 1;
candidateKillers.push_back(Killer(i, curNum, 1));
}
}
return ans;
}
private:
struct Killer {
Killer(int idx, int num, int iters)
: idx(idx), num(num), iters(iters) {};
int idx;
int num;
int iters;
};
int searchKiller(vector<Killer>& candidateKillers, int n) {
int lo = 0;
int hi = candidateKillers.size() - 1;
if (candidateKillers[0].num < n) {
return -1;
}
int ans = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (candidateKillers[mid].num > n) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
};
int main() {
vector<int> array1 = {10, 9, 7, 8, 6, 5, 3, 4, 2, 1};
vector<int> array2 = {1, 2, 3, 4};
vector<int> array3 = {4, 2, 1, 2, 3, 3};
cout << Solution().countIterations(array1) << endl; // 2
cout << Solution().countIterations(array2) << endl; // 0
cout << Solution().countIterations(array3) << endl; // 4
}
You can iterate in reverse, keeping two iterators or indices and moving elements in place. You don't need to allocate a new vector or even resize existing vector. Also a minor, but can replace recursion with loop or write the code the way compiler likely to do it.
This approach is still O(n^2) worst case but it would be faster in run time.

Number of subsequences of size k and sum s

Consider an array A with length n. Let k be the length of subsequences to be generated. What I want to do is to get the number of subsequences with length k and sum s.
Example:
A = [1,1,2,2,3]
s = 4
k = 2
So output would be 3 -> [{1,3}, {1,3}, {2,2}].
Note: 1 is considered twice as treated individually.
The total number of subsequences with length k is āæCā‚– (Here, 10).
What I tried: I tried to generate all subsequences of length k using Pascals Identity, individually calculate their sum and check whether it is equal to sum s or not. How can I make the algorithm more efficient?
Can anyone help me with this?
I don't know much about C++ but this seems to work:
#include <iostream>
using namespace std;
#include <map>
double f(int A[], int n, int s, int k, int i, map<array<int, 3>, double> memo){
if (k == 0)
return s == 0 ? 1 : 0;
if (i == n || s < 0 || k < 0)
return 0;
return memo[array<int, 3>{s, k, i}] =
f(A, n, s - A[i], k - 1, i + 1, memo) + f(A, n, s, k, i + 1, memo);
}
int main(){
map<array<int, 3>, double> memo;
int A[5] = {1, 1, 2, 2, 3};
double result = f(A, 5, 4, 2, 0, memo);
cout << result;
}
This can be solved using the knapsack method. For each element you can either include it or exclude it. Here's the cpp code :
ll knap(int values[],int n, int i, int length, int sum) { //ll is long long
if(s<0 || i>n-1 ||l<0) return 0;
if(s==0 && l==0) return 1;
ll a = knap(values,n,i+1,l-1,s-values[i]); //including current element
ll b = knap(values,n,i+1,l,s); //not including the current element and moving on
return (a+b);
}

Function to count same numbers in array C++

I have this function that is supposed to count how many duplicates of same number occur in certain array. Important this must be of complexity O(logn).
I wrote this one below, but it doesn't count the duplicates properly.
Also one more thing, the numbers are sorted from lowest to highest.
int CountElementsinFile(int *Arr, int num, int numOfD)
{
int avg{};
int inB = 0;
int inE = numOfD - 1;
int el{};
while (inB <= inE)
{
avg = (inB + inE) / 2;
if (Arr[avg] == num)
el++;
if (Arr[avg] > num)
inE = avg - 1;
else
inB = avg + 1;
}
return el;
}
With std, you might do:
int CountElementsinFile(const int *a, int size, int value)
{
const auto range = std::equal_range(a, a + size, value);
return range.second - range.first;
}
You need to determine the upper and lower boundaries of num subsequence using the Bisection method. You need to rearrange the lower or upper (depending on the comparison) search region boundary in the while loop until inB < inE reducing the region in half. The complexity will be O(ln(n)). You were close, but you will not be able to find both boundaries in one while loop. I just corrected your code.
int CountElementsinFile(int *Arr, int num, int numOfD)
{
// There is no num in the array
if (Arr[0] > num || Arr[numOfD - 1] < num)
return 0;
int avg{};
int lb, ub;
// Find lower boundary
int inB = 0;
int inE = numOfD - 1;
while (inB < inE)
{
// divide search region
avg = (inB + inE) / 2;
if (Arr[avg] >= num)
inE = avg;
else
inB = avg+1;
}
lb = inE;
// Find upper boundary
// inB already found
inE = numOfD - 1;
while (inB < inE)
{
avg = (inB + inE + 1) / 2;
if (Arr[avg] > num)
inE = avg-1;
else
inB = avg;
}
ub = inB;
return ub - lb + 1;
}
int main()
{
int arr[] = { 5, 7, 8, 9, 9, 9, 9, 9, 11 };
std::cout << CountElementsinFile(arr, 9, sizeof(arr) / sizeof(int)) << std::endl;
return 0;
}
From the function signature you gave, I am guessing you were given a number N and a sorted array of numbers and need to count how many times N appears in the array.
Since the array is sorted, you need to find the index (using binary search) of the first number that is smaller than N (this is O(log n)), the index of the first number larger than N (this is also O(log n)), and simply substract one index from the other.
Of course you need to take into account edge cases where there are no numbers smaller than N or larger than N, but that is for you to figure out.
#include<algorithm>
using namespace std;
int CountElementsinFile(int arr[], int size, int numToSearch)
{
return count(arr, arr + size, numToSearch);
}

Knapsack - How to identify which weights are used?

I have a code which gives the maximum value I can get by filling the knapsack with the optimal set of weights.
int arr[5] = {0, 0, 0, 0, 0};
int Weight[5] = {2, 5, 8, 7, 9};
int Value[5] = {4, 5, 7, 9, 8};
const int n = 5;
const int maxCapacity = 20;
int maximum(int a, int b)
{
return a > b ? a : b;
}
int knapsack(int capacity, int i)
{
if (i > n-1) return 0;
if (capacity < Weight[i])
{
return knapsack(capacity, i+1);
}
else
{
return maximum (knapsack(capacity, i+1),
knapsack(capacity - Weight[i], i+1) + Value[i]);
}
}
int main (void)
{
cout<<knapsack(maxCapacity,0)<<endl;
return 0;
}
I need to extend this solution by printing which all weights are used to find the optimal solution. For this I plan to use an array arr initialized to 0. Whenever a weight is used I mark the corresponding position in arr by 1, otherwise it remains 0.
First thing that came into my mind is to change the maximum() function like shown below
int maximum(int a, int b, int i)
{
if (a > b)
{
if (arr[i] == 1) arr[i] = 0;
return a;
}
else
{
if (arr[i] == 0) arr[i] = 1;
return b;
}
}
But even this solution fails for some combination of weights and values. Any suggestions on how to go forward?
The problem is that you dont know which one of the two options are selected by this command
return maximum (knapsack(capacity, i+1),
knapsack(capacity - Weight[i], i+1) + Value[i]);
I guess you can use two variables to store the values and if the weight is selected( the value of that variable is bigger ) you can add it to the array . Hope that solves your problem.

DP - Counting coin change

The problem requires to count number of coin changes for a particular cost.
For example, if I have coin values of 50, 20, 10, 5, 1, I can form costs of:
5 => (5), (11111), which are 2 ways.
10 => (10), (5, 5), (5, 11111), (11111, 11111), which are 4 ways.
Here is my function. It is returning wrong results begging from cost of 10 (returns 9 ways while the actual number of ways is only 4)
int dp[10000];
int coins[] = { 50, 20, 10, 5, 1 };
int rec(int n)
{
if (n == 0) return 1;
if (dp[n] != -1) return dp[n];
int cnt = 0;
for (int i = 0; i < 5; i++)
if (coins[i] <= n) cnt += rec(n - coins[i]);
return dp[n] = cnt;
}
How can I fix this function to give the correct number of ways? Is this algorithm correct even? see the complete code and its output here
NOTE: my problem is not with dp array initialization. I am using memset to initialize it to -1 each time before calling rec.
(5, 1, 1, 1, 1, 1) and (1, 1, 1, 5, 1, 1) is different way in you algorithm, you should keep it decreasing.
int dp[10000][5]; // dp[20][2] means, if the biggest coin is coins[2],
// how much ways for 20 ?
int coins[] = { 1, 5, 10, 20, 50 }; // here
int rec(int n, int m)
{
int cnt = 0;
int i;
if (n == 0) return 1;
//if (m == 0) return 1;
if (dp[n][m] != -1) return dp[n][m];
for (i = 0; i <= m; i++)
if (coins[i] <= n) cnt += rec(n - coins[i], i);
return dp[n][m] = cnt;
}
int main()
{
memset(dp, -1, sizeof(dp));
printf("%d\n", rec(10, 4));
}
The result is wrong since you never make sure that your algorithm starts with the 5 coin. (5,11111) is just as valid in your code as (1, 5, 1111), but this is the same result. Your result should be wrong from 6 and higher, not 10 and higher.
To fix this you can do like a cutoff in your function rec():
int rec(int n, int cutoff)
{
if (n == 0) return 1;
if (dp[n] != -1) return dp[n];
int cnt = 0;
for (int i = cutoff; i < 5; i++)
if (coins[i] <= n) cnt += rec(n - coins[i], i);
return dp[n] = cnt;
}
Should do it.
Edit: you will have to take care of your dp[] array, since it does not care about this cutoff, but this in general is the fault you are running into. You could comment that line, and check if this works.
One remark: Your initialization
memset(dp, -1, sizeof dp);
is not really safe. memset initializes every byte of a memory space (see http://www.cplusplus.com/reference/clibrary/cstring/memset/.). For this particular case you are lucky and the representation of int(-1) is (probably) the same of four times unsigned char(-1).
I would suggest using std::fill ( http://www.cplusplus.com/reference/algorithm/fill/ ).