Knapsack - How to identify which weights are used? - c++

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.

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.

Understanding the recursive knapsack

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?

How do I recursively enumerate all subsets adding to a given sum?

When I try to get the output, it shows same solutions with same element for few times before moving on to another one.
I want to get different solutions from the array that is equal to sum
For instance,
Solution 1 = 14, 8
Solution 2 = 14, 5, 3
Solution 3 = 13, 9
and so on but what I get is repeated solutions for the sum of 22. Im getting this error
Solution : { 14, 8 }
Solution : { 14, 8 }
Solution : { 14, 8 }
Solution : { 14, 8 }
Solution : { 14, 8 }
Solution : { 14, 8 }
Solution : { 14, 5, 3 }
Solution : { 13, 6, 3 }
Solution : { 13, 5, 4 }
Solution : { 13, 5, 4 }
Solution : { 13, 6, 3 }
Solution : { 13, 5, 4 }
Solution : { 13, 5, 4 }
and another 20 lines of the same output.
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <vector>
#include <algorithm>
using namespace std;
void printSolution(int* r, int k)
{
cout << " Solution : { ";
for (int j = 0; j < k; j++)
{
cout << r[j];
if (j < k - 1)
cout << ", ";
}
cout << " }" << endl;
}
bool subSetSum(int* a, int n, int i, int sum, int* r, int k)
{
if (sum == 0)
printSolution(r, k);
if (i > n)
return false;
r[k++] = a[i];
if (subSetSum(a, n, i + 1, sum - a[i], r, k))
return true;
k -= 1;
subSetSum(a, n, i + 1, sum, r, k);
}
int descendingOrder(const void* a, const void* b)
{
int* p1 = (int*)a;
int* p2 = (int*)b;
return *p2 - *p1;
}
int main()
{
int a[] = { 4, 10, 7, 12, 6, 10, 10, 8, 5, 13, 13, 11, 3, 14 };
int n = 14;
int* r = new int[n];
int k = 0;
qsort(a, n, sizeof(int), descendingOrder);
cout << " Array a[] = ";
for (int count = 0; count < n; count++)
{
cout << a[count] << " ";
}
cout << endl << endl;
int sum = 22;
bool solFound = subSetSum(a, n, 0, sum, r, k);
return 0;
}
You have several errors in your subsetSum function. First of all, your version has a branch where it doesn't return a value. This could have been easily mitigated by enabling compiler warnings.
Second, you have an off-by-one error in your termination condition. i==n is an invalid index, so you will run-over your buffer end.
The reason you get the same result several times is because there are multiple paths to the same result. This is most likely related to the missing return statement.
The fix for this is to terminate the recursion descend once you find a match (when you print it). It is guaranteed that you will not find additional results (unless there are entries <= 0 in your input).
bool subSetSum(int* a, int n, int i, int sum, int* r, int k)
{
if (sum == 0) {
printSolution(r, k);
return true;
}
if (i >= n)
return false;
r[k] = a[i];
bool found = subSetSum(a, n, i + 1, sum - a[i], r, k + 1);
found = subSetSum(a, n, i + 1, sum, r, k) || found;
return found;
}
Please note that this still finds duplicate solutions for duplicate values in your input array (such as 10). You can easily add a check to skip duplicate values in the second recursion call to subSetSum by not passing i + 1 but by finding the next index that is not duplicate. Since you already sorted your input, this can be done by incrementing i until it points to a different value.
Also it should be pointed out that this is quite unidiomatic C++. A better interface to your subsetSum would look like this:
template <typename T, typename ConstIterator>
bool subSetSum(ConstIterator begin, ConstIterator end, T sum, std::vector<T>& buf);
template <typename T, typename ConstIterator>
bool subSetSum(ConstIterator begin, ConstIterator end, T sum) {
std::vector<T> buf;
return subSetSum(begin,end,std::move(T),buf);
}
First of all, no effeciency can be applied here (probably), since this question is NP complete, which means it is probably not solvable in polynomial time.
About the solution, I'll attach a code:
using SolutionT = std::vector<std::set<std::size_t>>;
SolutionT subsetSum(const std::vector<int>& array, int requiredSum, std::size_t index, int currentSum)
{
if (currentSum > requiredSum) { // Remove it if negative integers are present
return {};
}
if (index >= array.size()) {
return {};
}
if (requiredSum == currentSum + array[index]) {
std::set<std::size_t> indices{};
indices.insert(index);
SolutionT solution;
solution.emplace_back(std::move(indices));
return solution;
}
auto includedSolutions = subsetSum(array, requiredSum, index + 1, currentSum + array[index]);
for (auto& solution : includedSolutions) {
solution.insert(index);
}
auto excludedSolutions = subsetSum(array, requiredSum, index + 1, currentSum);
std::copy(std::make_move_iterator(includedSolutions.begin()),
std::make_move_iterator(includedSolutions.end()),
std::back_inserter(excludedSolutions));
return excludedSolutions;
}
SolutionT subsetSum(const std::vector<int>& array, int requiredSum)
{
return subsetSum(array, requiredSum, 0, 0);
}
The code is rather complicated since you need an exponential number of elements, so it is very hard to do without C++ containers.

Function to return count of duplicate numbers in sorted array

I want to return the number of duplicate values in a sorted array.
For example: a = { 1, 1, 2, 3, 4, 4 }, fratelli(n) should return 2. (They are 1, 1 and 4, 4)
I attempted to use a recursive approach, but it doesn't work. It Always give me 4.
I'm asking if someone please could help me understand better this methods of programming. Thanks a lot!
The function:
#include <iostream>
using namespace std;
int fratelli(int a[], int l, int r)
{
if (l == r) return 0;
else
{
int c = (l+r) / 2;
int n = fratelli(a, l, c) + fratelli(a, c+1, r);
if (a[l] == a[l+1]) n++;
return n;
}
}
int main()
{
const int _N = 11;
int array[_N] = { 1, 1, 2, 3, 5, 5, 7, 8, 8, 11, 12 };
cout << "\n" << fratelli(array, 0, _N-1);
return 0;
}
You have an error on this line:
if (a[l] == a[l+1]) n++;
The check should be at index c not at l. Apart from this your code seems alright to me.

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/ ).