How to remove all element between given indexes? - c++

Briefly about the condition of the problem:
Given the numbers from 1 to n, and m stages of purification, after that follows in m lines with two numbers left and right (borders, inclusive), the range of deleting numbers (1..n), you must output all living elements after removal.
I will give an example:
n = 10, m = 3
Suppose we make an array a[1,2,3,4,5,6,7,8,9,10];
left = 1, right = 2;
After 1 deletion: a[3,4,5,6,7,8,9,10];
left = 4, right = 5;
After 2 deletion: a[3,4,5,8,9,10];
left = 3, right = 5;
After 3 deletion: a[3,4,10];
Conclusion: 3 4 10
So not everything is so simple, the restrictions are strict, namely:
n, m <= 3 * 10 ^ 5
left <= right
My attempt was as follows: I created a vector from numbers from 1 to n and deleted all elements in range [left, right], but Time Limit is coming because of the complexity.
#include <iostream>
#include <vector>
using namespace std;
#define ll uint64_t
int main() {
ll i, n, k, l, r;
cin >> n >> k;
vector <ll> a;
for (i = 1; i <= n; i++) {
a.push_back(i);
}
for (i = 1; i <= k; i++) {
cin >> l >> r;
a.erase(a.begin()+l-1,a.begin()+r);
}
cout << a.size() << endl;
for (auto i : a) {
cout << i << ' ';
}
}
How to solving this problem?

The problem is solvable using a segment tree with lazy propagation and order statistics in O((N + Q) * log(N)) which should pass in a second or two on most online judges given your constraints.
Brief Explanation
'Still Exists' Boolean Array
Let's imagine that we have a boolean array of size N that indicates for each item whether it still exists or removed. The array will be initialized with ones since no elements are removed yet.
Segment Tree
Info Query: Let's build a range-sum segment tree on top of this boolean array (True is mapped to 1 and false is mapped to 0). If we query for any [L, R] range, the segment tree answers with the number of still existing elements. (Note that L and R are indices in the original array -that includes removed and non-removed elements-)
Update Query: The only update query done on the segment tree is to set a range with zeros (marking a range of elements as removed). Since we update a range of elements to zeros, we need to use lazy propagation (No need for it if the problem required removing a single item).
The Final Output
After updating all ranges given to zeros, we can iterate over each index and check if it's zero or one, and print it if it's one, however the solution is not that easy since the ranges provided in the input are not ranges in the original array, it's actually indices in the updated array.
Updated Ranges Problem
To understand the problem more let's go through an example:
Let's assume we're working with an array of length 6, the array is initially: 1 2 3 4 5 6 and the boolean array is initially: 1 1 1 1 1 1
Let's assume that the first deletion is [2, 4], now the new array is: 1 5 6 and the new updated boolean array is: 1 0 0 0 1 1
At this point, if we were asked to print the array, we will simply go through the original array and print the values that only corresponds to true in the boolean array.
Now let's try to delete another range [1, 2], if we simply set the first two elements to zeros, then we will end up with: 0 0 0 0 1 1. Which means that we still have 5, 6 on in our array, while we actually have only 6 after the last deletion.
Order-Statistics to solve the updated ranges problem
To solve the problem, we need to add the order-statistics property to our segment tree. this property will answer the following question: Given X, find the index were the prefix sum of ones that ends with it is X, this will help us map the current [L, R] into new [L, R] that can be used with the original indexing.
To understand the mapping better, let's go back to the second step of our example:
The boolean array was: 1 0 0 0 1 1, Delete elements between L=1 and R=2, using the order-statistics property, L will be mapped to 1 and R will be mapped to 5, now we will update the range between the newL and the newR to zeros and the boolean array becomes 0 0 0 0 0 1.
Code
#include <bits/stdc++.h>
using namespace std;
class SegmentTree {
vector<int> seg, lazy;
int sz;
void build(int ind, int ns, int ne, const vector<int> &v) {
if (ns == ne) {
seg[ind] = v[ns];
return;
}
int mid = ns + (ne - ns) / 2;
build(ind * 2, ns, mid, v);
build(ind * 2 + 1, mid + 1, ne, v);
seg[ind] = seg[ind * 2] + seg[ind * 2 + 1];
}
void probagateLazy(int ind) {
if (lazy[ind]) {
lazy[ind] = 0, seg[ind] = 0;
if (ind * 2 + 1 < 4 * sz)
lazy[ind * 2] = lazy[ind * 2 + 1] = 1;
}
}
int query(int s, int e, int ind, int ns, int ne) {
probagateLazy(ind);
if (e < ns || s > ne)
return 0;
if (s <= ns && ne <= e)
return seg[ind];
int mid = ns + (ne - ns) / 2;
return query(s, e, ind * 2, ns, mid) + query(s, e, ind * 2 + 1, mid + 1, ne);
}
void update(int s, int e, int ind, int ns, int ne) {
probagateLazy(ind);
if (e < ns || s > ne)
return;
if (s <= ns && ne <= e) {
lazy[ind] = 1;
probagateLazy(ind);
return;
}
int mid = ns + (ne - ns) / 2;
update(s, e, ind * 2, ns, mid);
update(s, e, ind * 2 + 1, mid + 1, ne);
seg[ind] = seg[ind * 2] + seg[ind * 2 + 1];
}
int find(int pos, int ind, int ns, int ne) {
probagateLazy(ind);
if (ns == ne)
return ns;
probagateLazy(ind * 2);
probagateLazy(ind * 2 + 1);
int mid = ns + (ne - ns) / 2;
if (pos <= seg[ind * 2])
return find(pos, ind * 2, ns, mid);
return find(pos - seg[ind * 2], ind * 2 + 1, mid + 1, ne);
}
public:
SegmentTree(int sz, const vector<int> &v) {
this->sz = sz;
seg = vector<int>(sz * 4);
lazy = vector<int>(sz * 4);
build(1, 0, sz - 1, v);
}
int query(int s, int e) {
return query(s, e, 1, 0, sz - 1);
}
int update(int s, int e) {
update(s, e, 1, 0, sz - 1);
}
int find(int pos) {
return find(pos, 1, 0, sz - 1);
}
};
int main() {
int i, n, k, l, r;
scanf("%d %d", &n, &k);
vector<int> a;
for (i = 1; i <= n; i++) {
a.push_back(i);
}
vector<int> v(n, 1);
SegmentTree st(n, v);
while (k--) {
scanf("%d %d", &l, &r);
int newL = st.find(l);
int newR = st.find(r);
st.update(newL, newR);
}
vector<int> ans;
for (int i = 0; i < n; i++) {
if (st.query(i, i))
ans.push_back(a[i]);
}
printf("%d\n", ans.size());
for (int i = 0; i < ans.size(); i++) {
printf("%d ", ans[i]);
}
}
Black Boxing the Segment Tree
If you're not familiar with segment trees, then it's expected to find the code hard to understand, so I'll try to make it easier by ignoring the internal implementation of the segment tree and give you a quick look at its functionalities.
Query Method
The query method takes as an input the start and end index of the range to be queried and returns the summation of the elements inside this range.
Update Method
The update method takes as input the start and end index of the range to be updated, and set all items inside this range to zeros
Find Method
The find method takes as an input X and returns the first element Y were the sum of elements in the range [0, Y] is X
Other Solutions
The problem can also be solved using Splay Tree or Treap data structure.

Related

How to solve this problem through dynamic programming approach?

I was solving a very basic problem on arrays. The problem statement goes like this:
A basketball court has been opened in SIS, so Demid has decided to hold a basketball exercise session. 2⋅n students have come to Demid's exercise session, and he lined up them into two rows of the same size (there are exactly n people in each row). Students are numbered from 1 to n in each row in order from left to right.
Now Demid wants to choose a team to play basketball. He will choose players from left to right, and the index of each chosen player (excluding the first one taken) will be strictly greater than the index of the previously chosen player. To avoid giving preference to one of the rows, Demid chooses students in such a way that no consecutive chosen students belong to the same row. The first student can be chosen among all 2n students (there are no additional constraints), and a team can consist of any number of students.
Demid thinks, that in order to compose a perfect team, he should choose students in such a way, that the total height of all chosen students is maximum possible. Help Demid to find the maximum possible total height of players in a team he can choose.
For example if the input is:(First line contains the value of n, next 2 rows follow which contains the height of the students)
5
9 3 5 7 3
5 8 1 4 5
My approach was:
#These are global variables and functions.
int arr1[n],arr2[n],sum=0,max=0;
void func1(i)
{
if(i==n)
{
if(sum>max)
max=sum;
return;
}
sum+=arr1[i];
for(k=i+1;k<n;k++)
func2(k);
}
void func2(i)
{
if(i==n)
{
if(sum>max)
max=sum;
return;
}
sum+=arr2[i];
for(k=i+1;k<n;k++)
func1(k);
}
#Caller module. In main
for(i=0;i<n;i++)
{
sum=0;
func1(i);
}
This is my algorithm based on my logical reasoning. I have not coded it yet, will code it later. So feel free to point out any logical errors in the code.
I know this can be solved easily using dynamic programming approach and this algorithm is not quite it. How will the functions look like in that case?
As far as I can point out, the problem in this algorithm is I need to declare arr1 and arr2 globally whereas I get to know the value of n in the main function.
Dynamic programming would be quite straightforward here. We have two choices: Choose from A or skip, Choose from B or skip. Our bottom-up recurrence could look like:
// Choose from A or skip
m[i][0] = max(A[i] + m[i - 1][1], m[i - 1][0])
// Choose from B or skip
m[i][1] = max(B[i] + m[i - 1][0], m[i - 1][1])
JavaScript code:
function f(A, B){
let m = new Array(A.length + 1)
for (let i=0; i<=A.length; i++)
// [a or skip, b or skip]
m[i] = [0, 0]
for (let i=1; i<=A.length; i++){
// Choose from A or skip
m[i][0] = Math.max(
A[i-1] + m[i - 1][1], m[i - 1][0])
// Choose from B or skip
m[i][1] = Math.max(
B[i-1] + m[i - 1][0], m[i - 1][1])
}
return Math.max(...m[A.length])
}
var a = [9, 3, 5, 7, 3]
var b = [5, 8, 1, 4, 5]
console.log(f(a, b))
We can define 2 functions A and B. A(i) is the maximum height we can get by next choosing player with index i or greater from the first row. B(i) is the same for the second row. Now we can write A in terms of B and B in terms of A. For example A(i) is the max over all indices k that are i or greater by choosing the k'th element from the first set plus the max we can get by choosing from k+1 or higher from the second. B(i) is symmetric:
A(i) = max_{k=i..n} a[k] + B(k + 1); A(n) = a[n]
B(i) = max_{k=i..n} b[k] + A(k + 1); B(n) = b[n]
The answer will be max(A(1), B(1)).
A simple way to go is just code this as it's written with 2 memoized functions. I'll use C rather than C++ with tweaking to use 0-based indices.
#include <stdio.h>
#define N 5
int a[] = {9, 3, 5, 7, 3};
int b[] = {5, 8, 1, 4, 5};
int Avals[N], Bvals[N];
int B(int i);
int A(int i) {
if (i >= N) return 0;
if (Avals[i]) return Avals[i];
int max = 0;
for (int k = i; k < N; ++k) {
int val = a[k] + B(k + 1);
if (val > max) max = val;
}
return Avals[i] = max;
}
int B(int i) {
if (i >= N) return 0;
if (Bvals[i]) return Bvals[i];
int max = 0;
for (int k = i; k < N; ++k) {
int val = b[k] + A(k + 1);
if (val > max) max = val;
}
return Bvals[i] = max;
}
int main(void) {
int aMax = A(0);
int bMax = B(0);
printf("%d\n", aMax > bMax ? aMax : bMax);
return 0;
}
I claim there's a way to replace the memoized recursion with simple loops that access the elements of Avals and Bvals in strictly decreasing index order, but I'll let you figure out the details. This result will be smaller, faster code.

Remove repeated elements and give a Range Sum

I have a question regarding already asked this question:
SPOJ DQUERY : TLE Even With BIT?
What if I would like to not consider the repeated element to count when I make a range query?
following is an example:
Input
Line 1: n (1 ≤ n ≤ 10^6).
Line 2: n numbers a1, a2, ..., an (-10^9 ≤ ai ≤ ).
Line 3: q (1 ≤ q ≤ 10^6), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j
representing a d-query (1 ≤ i ≤ j ≤ n).
Output
For each d-query (i, j), print the number of distinct elements in the
subsequence ai, ai+1, ..., aj in a single line.
Example
Input
9
1 2 3 2 4 1 2 3 4
3
1 9
2 4
5 9
2 7
Output
0 //Explanation: all elements have been repeated.
1 //Explanation: only 3 has not repeated.
3 //Explanation: only 4 is repeated, so count only 1, 2 and 3.
3 //Explanation: only 2 is repeated, so count only 3, 4 and 1.
what could it be the necessary changes which should have done in #kraskevich 's answer(which is an efficient solution for that particular case)? I tried to add 0 in BIT, instead of -1 in above-mentioned solution, which does not help for all types of queries. Can anybody get me an idea?
I finally made it using MOs Algorithm. Following will work as expected.
/** For the given set of queries find the Unique element
* count of an array using MO's Algorithum. */
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <unordered_map>
using namespace std;
struct Query // struct for storing the queries
{
int Left;
int Right;
int Index;
};
inline void Add(const int i, int &ans, vector<int> &Arr, vector<int> &countArray)
{
++countArray[Arr[i]];
if(countArray[Arr[i]] == 1)
ans += countArray[Arr[i]];
if(countArray[Arr[i]] == 2)
ans -= countArray[Arr[i]];
}
inline void Remove(const int i, int &ans, vector<int> &Arr, vector<int> &countArray)
{
--countArray[Arr[i]];
if(countArray[Arr[i]] == 1)
ans += countArray[Arr[i]];
if(countArray[Arr[i]] == 0)
ans -= countArray[Arr[i]];
}
int main()
{
int _size; cin >> _size;
vector<int> Arr; Arr.reserve(_size);
copy_n(istream_iterator<int>(cin), _size, back_inserter(Arr));
//copy(Arr.cbegin(), Arr.cend(), ostream_iterator<int>(cout, "\t"));
int id = -1;
int sqrt_n = sqrt(_size);
int Q; cin >> Q;
vector<Query> qArr(Q);
unordered_map<int, int> Map;
for (int i = 0; i < _size; ++i)
{
if (Map.count(Arr[i]) == 0)
Map[Arr[i]] = ++id;
Arr[i] = Map[Arr[i]];
}
// read queries
for (int i = 0; i < Q; ++i)
{
int L,R;
cin >> L >> R;
qArr[i].Left = L-1;
qArr[i].Right = R-1;
qArr[i].Index = i;
}
// sort the queries according to(MO's Algorithum)
sort(qArr.begin(),qArr.end(),
[&](const Query &lhs, const Query &rhs)->bool
{
return ( (lhs.Left/sqrt_n == rhs.Left/sqrt_n) ?
lhs.Right < rhs.Right: // Qs with same Left case
(lhs.Left / sqrt_n) < (rhs.Left / sqrt_n) ); // Qs with diff values case
});
int currStart = 0;
int currEnd = 0;
int tempAnswer= 0;
vector<int> Answer(Q);
vector<int> countArray(_size);
for (int i = 0; i < Q; ++i)
{
int L = qArr[i].Left;
int R = qArr[i].Right;
/** Remove extra elements of previous range. For
* example if previous range is [0, 3] and current
* range is [2, 5], then a[0] and a[1] are subtracted */
while (currStart < L)
{
Remove(currStart, tempAnswer, Arr, countArray);
++currStart;
}
/** Add Elements of current Range */
while (currStart > L)
{
Add(currStart - 1, tempAnswer, Arr, countArray);
--currStart;
}
while (currEnd <= R)
{
Add(currEnd, tempAnswer, Arr, countArray);
++currEnd;
}
/** Remove elements of previous range. For example
* when previous range is [0, 10] and current range
* is [3, 8], then a[9] and a[10] are subtracted */
while (currEnd > R + 1)
{
Remove(currEnd - 1, tempAnswer, Arr, countArray);
--currEnd;
}
Answer[qArr[i].Index] = tempAnswer;
}
for(const auto &it: Answer) cout<<it<<endl;
return 0;
}

String decode: looking for a better approach

I have worked out a O(n square) solution to the problem. I was wondering about a better solution to this. (this is not a homework/interview problem but something I do out of my own interest, hence sharing here):
If a=1, b=2, c=3,….z=26. Given a string, find all possible codes that string
can generate. example: "1123" shall give:
aabc //a = 1, a = 1, b = 2, c = 3
kbc // since k is 11, b = 2, c= 3
alc // a = 1, l = 12, c = 3
aaw // a= 1, a =1, w= 23
kw // k = 11, w = 23
Here is my code to the problem:
void alpha(int* a, int sz, vector<vector<int>>& strings) {
for (int i = sz - 1; i >= 0; i--) {
if (i == sz - 1) {
vector<int> t;
t.push_back(a[i]);
strings.push_back(t);
} else {
int k = strings.size();
for (int j = 0; j < k; j++) {
vector<int> t = strings[j];
strings[j].insert(strings[j].begin(), a[i]);
if (t[0] < 10) {
int n = a[i] * 10 + t[0];
if (n <= 26) {
t[0] = n;
strings.push_back(t);
}
}
}
}
}
}
Essentially the vector strings will hold the sets of numbers.
This would run in n square. I am trying my head around at least an nlogn solution.
Intuitively tree should help here, but not getting anywhere post that.
Generally, your problem complexity is more like 2^n, not n^2, since your k can increase with every iteration.
This is an alternative recursive solution (note: recursion is bad for very long codes). I didn't focus on optimization, since I'm not up to date with C++X, but I think the recursive solution could be optimized with some moves.
Recursion also makes the complexity a bit more obvious compared to the iterative solution.
// Add the front element to each trailing code sequence. Create a new sequence if none exists
void update_helper(int front, std::vector<std::deque<int>>& intermediate)
{
if (intermediate.empty())
{
intermediate.push_back(std::deque<int>());
}
for (size_t i = 0; i < intermediate.size(); i++)
{
intermediate[i].push_front(front);
}
}
std::vector<std::deque<int>> decode(int digits[], int count)
{
if (count <= 0)
{
return std::vector<std::deque<int>>();
}
std::vector<std::deque<int>> result1 = decode(digits + 1, count - 1);
update_helper(*digits, result1);
if (count > 1 && (digits[0] * 10 + digits[1]) <= 26)
{
std::vector<std::deque<int>> result2 = decode(digits + 2, count - 2);
update_helper(digits[0] * 10 + digits[1], result2);
result1.insert(result1.end(), result2.begin(), result2.end());
}
return result1;
}
Call:
std::vector<std::deque<int>> strings = decode(codes, size);
Edit:
Regarding the complexity of the original code, I'll try to show what would happen in the worst case scenario, where the code sequence consists only of 1 and 2 values.
void alpha(int* a, int sz, vector<vector<int>>& strings)
{
for (int i = sz - 1;
i >= 0;
i--)
{
if (i == sz - 1)
{
vector<int> t;
t.push_back(a[i]);
strings.push_back(t); // strings.size+1
} // if summary: O(1), ignoring capacity change, strings.size+1
else
{
int k = strings.size();
for (int j = 0; j < k; j++)
{
vector<int> t = strings[j]; // O(strings[j].size) vector copy operation
strings[j].insert(strings[j].begin(), a[i]); // strings[j].size+1
// note: strings[j].insert treated as O(1) because other containers could do better than vector
if (t[0] < 10)
{
int n = a[i] * 10 + t[0];
if (n <= 26)
{
t[0] = n;
strings.push_back(t); // strings.size+1
// O(1), ignoring capacity change and copy operation
} // if summary: O(1), strings.size+1
} // if summary: O(1), ignoring capacity change, strings.size+1
} // for summary: O(k * strings[j].size), strings.size+k, strings[j].size+1
} // else summary: O(k * strings[j].size), strings.size+k, strings[j].size+1
} // for summary: O(sum[i from 1 to sz] of (k * strings[j].size))
// k (same as string.size) doubles each iteration => k ends near 2^sz
// string[j].size increases by 1 each iteration
// k * strings[j].size increases by ?? each iteration (its getting huge)
}
Maybe I made a mistake somewhere and if we want to play nice we can treat a vector copy as O(1) instead of O(n) in order to reduce complexity, but the hard fact remains, that the worst case is doubling outer vector size in each iteration (at least every 2nd iteration, considering the exact structure of the if conditions) of the inner loop and the inner loop depends on that growing vector size, which makes the whole story at least O(2^n).
Edit2:
I figured out the result complexity (the best hypothetical algoritm still needs to create every element of the result, so result complexity is like a lower bound to what any algorithm can archieve)
Its actually following the Fibonacci numbers:
For worst case input (like only 1s) of size N+2 you have:
size N has k(N) elements
size N+1 has k(N+1) elements
size N+2 is the combination of codes starting with a followed by the combinations from size N+1 (a takes one element of the source) and the codes starting with k, followed by the combinations from size N (k takes two elements of the source)
size N+2 has k(N) + k(N+1) elements
Starting with size 1 => 1 (a) and size 2 => 2 (aa or k)
Result: still exponential growth ;)
Edit3:
Worked out a dynamic programming solution, somewhat similar to your approach with reverse iteration over the code array and kindof optimized in its vector usage, based on the properties explained in Edit2.
The inner loop (update_helper) is still dominated by the count of results (worst case Fibonacci) and a few outer loop iterations will have a decent count of sub-results, but at least the sub-results are reduced to a pointer to some intermediate node, so copying should be pretty efficient. As a little bonus, I switched the result from numbers to characters.
Another edit: updated code with range 0 - 25 as 'a' - 'z', fixed some errors that led to wrong results.
struct const_node
{
const_node(char content, const_node* next)
: next(next), content(content)
{
}
const_node* const next;
const char content;
};
// put front in front of each existing sub-result
void update_helper(int front, std::vector<const_node*>& intermediate)
{
for (size_t i = 0; i < intermediate.size(); i++)
{
intermediate[i] = new const_node(front + 'a', intermediate[i]);
}
if (intermediate.empty())
{
intermediate.push_back(new const_node(front + 'a', NULL));
}
}
std::vector<const_node*> decode_it(int digits[9], size_t count)
{
int current = 0;
std::vector<const_node*> intermediates[3];
for (size_t i = 0; i < count; i++)
{
current = (current + 1) % 3;
int prev = (current + 2) % 3; // -1
int prevprev = (current + 1) % 3; // -2
size_t index = count - i - 1; // invert direction
// copy from prev
intermediates[current] = intermediates[prev];
// update current (part 1)
update_helper(digits[index], intermediates[current]);
if (index + 1 < count && digits[index] &&
digits[index] * 10 + digits[index + 1] < 26)
{
// update prevprev
update_helper(digits[index] * 10 + digits[index + 1], intermediates[prevprev]);
// add to current (part 2)
intermediates[current].insert(intermediates[current].end(), intermediates[prevprev].begin(), intermediates[prevprev].end());
}
}
return intermediates[current];
}
void cleanupDelete(std::vector<const_node*>& nodes);
int main()
{
int code[] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
int size = sizeof(code) / sizeof(int);
std::vector<const_node*> result = decode_it(code, size);
// output
for (size_t i = 0; i < result.size(); i++)
{
std::cout.width(3);
std::cout.flags(std::ios::right);
std::cout << i << ": ";
const_node* item = result[i];
while (item)
{
std::cout << item->content;
item = item->next;
}
std::cout << std::endl;
}
cleanupDelete(result);
}
void fillCleanup(const_node* n, std::set<const_node*>& all_nodes)
{
if (n)
{
all_nodes.insert(n);
fillCleanup(n->next, all_nodes);
}
}
void cleanupDelete(std::vector<const_node*>& nodes)
{
// this is like multiple inverse trees, hard to delete correctly, since multiple next pointers refer to the same target
std::set<const_node*> all_nodes;
for each (auto var in nodes)
{
fillCleanup(var, all_nodes);
}
nodes.clear();
for each (auto var in all_nodes)
{
delete var;
}
all_nodes.clear();
}
A drawback of the dynamically reused structure is the cleanup, since you wanna be careful to delete each node only once.

Perfect Square in Leetcode

I am having trouble understanding one of a Leetcode Problem.
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
Solution:
int numSquares(int n) {
static vector<int> dp {0};
while (dp.size() <= n) {
int m = dp.size(), squares = INT_MAX;
for (int i=1; i*i<=m; ++i)
squares = min(squares, dp[m-i*i] + 1);
dp.push_back(squares);
}
return dp[n];
}
I really dont understand what is going on with min(squares,dp[m-i*i]+1). Can you please explain?
thx.
I had a hard time with this too. Let's take the example number n=13.
First thing to observe is that: 1^2 =1, 2^2=4, 3^2=9, 4^2=16
So 13 can't be composed of anything greater than
3^2. Generically speaking, n can only be composed of numbers 1 to sqrt(n)
So we are left with some combination of the square of the following numbers: 1, 2, or 3.
Next thing we want to do is come up with the recursive formula. This took me a long time to understand. But we basically want to dwindle down to work with a smaller n (that's the whole point of recursion). We do that by subtracting our candidate perfect squares from n. For example:
If we try 3, then dp(13)=dp(13-3^2)+1=dp(4)+1.
The +1 is incrementing the count by 1 and is from the the fact that we already took off a perfect square from 13, which was the 3^2. Each +1 is a perfect square that we took off.
If we try 2, then dp(13)=13-2^2=dp(9)+1
If we try 1, then dp(13)=13-1^2=dp(12)+1
So we are left with comparing which is the smallest out of dp(4), dp(9), and dp(12). Hence the min.
The solution, which you have mentioned, is the bottom-up version of the algorithm. In order to understand the algorithm better, I would advice to look at the top-down version of the solution.
Let's look closer at the recurrence relation for the calculation of the minimal amount of the perfect squares, contained inside the number N. For given N and any arbitrary number x (which is the candidate for being considered as the member of the shortest sequence of numbers, whose perfect squares sums-up to N):
f(N, x) = 0 , if N = 0 ;
f(N, x) = min( f(N, x + 1), f(N - x^2, 1) ) , if N >= x^2 ;
f(N, x) = +infinity , otherwise ;
solution(N) = f(N, 1)
Now, having in mind the considered recurrence, we can construct the top-down solution (I will implement it in Java):
int solve(int n) {
return solve(n, 1);
}
int solve(int n, int curr) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
// if curr belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int inclusive = solve(n - (curr * curr), 1) + 1;
// otherwise:
int exclusive = solve(n, curr + 1);
return Math.min(exclusive, inclusive);
}
The runtime complexity of the given solution is exponential.
However, we can notice that there are only [1..n] possible values of n and [1..sqrt(n)] values of curr. Which, implies, that there are only n * sqrt(n) combinations of different values of arguments of the function solve. Hence, we can create the memoization table and reduce the complexity of the top-down solution:
int solve(int n) {
// initialization of the memoization table
int[][] memoized = new int[n + 1][(int) (Math.sqrt(n) + 1)];
for (int[] row : memoized) {
Arrays.fill(row, NOT_INITIALIZED);
}
return solve(n, 1, memoized);
}
int solve(int n, int curr, int[][] memoized) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
if (memoized[n][curr] != NOT_INITIALIZED) {
// the sub-problem has been already solved
return memoized[n][curr];
}
int exclusive = solve(n, curr + 1, memoized);
int inclusive = solve(n - (curr * curr), 1, memoized) + 1;
memoized[n][curr] = Math.min(exclusive, inclusive);
return memoized[n][curr];
}
Given solution has the runtime complexity O(N * sqrt(N)).
However, it is possible to reduce the runtime complexity to O(N).
As far as the recurrence relation for f(N, x) depends only on f(N, x + 1) and f(N - x^2, 1) - it means, that the relation can be equivalently transformed to the loop form:
f(0) = 0
f(N) = min( f(N - x^2) + 1 ) , across the all x, such that x^2 <= N
In this case we have to memoize the f(N) only for N different values of its argument.
Hence, below presented the O(N) top-down solution:
int solve_top_down_2(int n) {
int[] memoized = new int[n + 1];
Arrays.fill(memoized, NOT_INITIALIZED);
return solve_top_down_2(n, memoized);
}
int solve_top_down_2(int n, int[] memoized) {
if (n == 0) {
return 0;
}
if (memoized[n] != NOT_INITIALIZED) {
return memoized[n];
}
// if 1 belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int result = solve_top_down_2(n - (1 * 1)) + 1;
for (int curr = 2; (curr * curr) <= n; curr++) {
// check, whether some other number belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
result = Math.min(result, solve_top_down_2(n - (curr * curr)) + 1);
}
memoized[n] = result;
return result;
}
Finally, the presented top-down solution can be easily transformed to the bottom-up solution:
int solve_bottom_up(int n) {
int[] memoized = new int[n + 1];
for (int i = 1; i <= n; i++) {
memoized[i] = memoized[i - (1 * 1)] + 1;
for (int curr = 2; (curr * curr) <= i; curr++) {
memoized[i] = Math.min(memoized[i], memoized[i - (curr * curr)] + 1);
}
}
return memoized[n];
}
The clarification to your confusion lies in the question itself. The structure dp holds the least number of squares that sum up to the index position of dp.
E.g., squares would return 3 when n=9, but least possible is 1, which is what dp[m- i*i] + 1 would return.

Find which items are taken in knapsack

solving the knapsack problem recursively, I want to know which items (item's weight) are taken in the bag that gives the maximum value.
so far I have this:
int MAX(int a, int b) { return (a > b) ? a : b ; }
int thief(int W, int weight[], int value[], int n)
{
int a,b,c;
//basecase:
if(n == 0 || weight <= 0) return 0;
// each item's weight can't be more than W:
if(weight[n-1] > W){
return thief(W, weight, value, n-1);}
a=value[n-1] + thief(W-weight[n-1], weight, value, n-1);// a: nth item included
b=thief(W, weight, value, n-1);// b:nth item not included
c= MAX(a,b);//answer is the maximum of situation a and b
if (c==a) { //if situation a occurs then nth item is included
cout<<weight[n]<<endl;
}
return c;
}
consider n=4 and maximum weight (W) = 30
let weights be : 30 10 20 5
and values : 100 50 60 10
but this code outputs: 20 5 20 10 5
I just want to output 10 and 20.
also I have tried to define a bool array with default values of false and its nth element changes to true if c==a occurs but this won't give the correct result as well.
I'm supposed to do it recursively.
Your basic algorithm doesn't work. You can't do the printing while you test different combinations.
However, first you must fix a bug:
cout<<weight[n-1]<<endl; // n-1 instead of n
Your algorithm does this:
a = value[3] + thief(30-weight[3], weight, value, 3); // Use item 3
b = thief(30, weight, value, 3); // Don't use item 3
The second line will lead to
a = value[2] + thief(30-weight[2], weight, value, 2); // Use item 2
b = thief(30, weight, value, 2); // Don't use item 2
The second line will lead to
a = value[1] + thief(30-weight[1], weight, value, 1); // Use item 1
b = thief(30, weight, value, 1); // Don't use item 1
The second line will lead to
a = value[0] + thief(30-weight[0], weight, value, 0); // Use item 0
b = thief(30, weight, value, 0); // Don't use item 0
This causes
a = 30
b = 0
so your code will select item 0 and print 30 but that is a bug!
So as I stated in the start: You can't do the printing while you test different combinations.
Instead you need to keep track of which elements you use in the different combinations and only keep the "best".
I haven't tested the code below but I think you can do it like this (assuming your code calculates the best combination correctly):
#include <vector>
// The vector v is used for holding the index of the items selected.
// The caller must supply a vector containing the items included so far.
// This function will test whether item "n-1" shall be included or
// excluded. If item "n-1" is included the index is added to the vector.
int thief(int W, int weight[], int value[], int n, vector<int>& v) // Added vector
{
vector<int> v1, v2; // Vector to hold elements of the two combinations
int a,b,c;
//basecase:
if(n == 0 || weight <= 0) return 0;
// each item's weight can't be more than W:
if(weight[n-1] > W){
return thief(W, weight, value, n-1, v2);}
v1.push_back(n-1); // Put n-1 in vector v1 and pass the vector v1
a=value[n-1] + thief(W-weight[n-1], weight, value, n-1, v1);// a: nth item included
// Don't put anything in v2 but pass the vector v2
b=thief(W, weight, value, n-1, v2);// b:nth item not included
c= MAX(a,b);//answer is the maximum of situation a and b
if (c==a) { //if situation a occurs then nth item is included
// cout<<weight[n-1]<<endl;
// Copy elements from v1 to v
for (auto e : v1)
{
v.push_back(e);
}
}
else
{
// Copy elements from v2 to v
for (auto e : v2)
{
v.push_back(e);
}
}
return c;
}
int main() {
vector<int> v;
int weight[4] = {30, 10, 20, 5};
int value[4] = {100, 50, 60, 10};
cout << "result=" << thief(30, weight, value, 4, v) << endl;
// Print the elements used
for (auto e : v)
{
cout << "elem=" << e << endl;
}
return 0;
}
Finally notice - your brute force method is very expensive in terms of execution time in the start value for n is high. There are much better ways to solve this problem.
can you write the code in c?
I wrote this but didn't work. (I think the difference is in the bold)
int knapSack(int W, int wt[], int val[], int n, int arr[])
{
int x, y, c, j, arr1[50], arr2[50];
// Base Case
if (n == 0 || W == 0)
return 0;
// If weight of the nth item is more than Knapsack capacity W, then
// this item cannot be included in the optimal solution
if (wt[n - 1] > W)
return knapSack(W, wt, val, n - 1, arr2);
// Return the maximum of two cases:
// x nth item included
// y not included
**arr1[n - 1] = val[n - 1];**
x = val[n - 1] + knapSack(W - wt[n - 1], wt, val, n - 1, arr1);
//copyArr(arr, out, n);
y = knapSack(W, wt, val, n - 1, arr2);
if (x > y)
c = x;
else
c = y;
if (c == x)
for (j = 0; j < 50; j++)
arr[j] = arr1[j];
else
for (j = 0; j < 50; j++)
arr[j] = arr2[j];
return c;
}