0-1 Knapsack for large weights - c++

I am trying to solve the 0-1 Knapsack for the large weights problem. Constraints for the problem are :
*All values in input are integers.
$$1 \leq N \leq 100$$
$$1 \leq W \leq 10^9$$
$$1 \leq w_i \leq W$$
$$1 \leq v_i \leq 10^3*$$
Below is the code which I have written with comments.
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int V = 1e5;
const int N = 100;
int main() {
int n, w, inp1, inp2, max_val = -1;
vector<pair<int, int>> arr;
cin >> n >> w; // n - no of items, w - total weight that the knapsack can hold
for(int i = 0; i < n; i++){
cin >> inp1 >> inp2; // ith input : w_i v_i
arr.push_back({inp1, inp2});
max_val = max(max_val, inp2); // storing maximum value of an item from the inputs
}
unsigned long long int dp[N + 1][V + 1]; // dp[i][j] = min weight that can be obtained from items 0..(i - 1) having total value atmost j
for(int i = 0; i <= n; i++){
for(int j = 0; j <= (max_val * n); j++){
if(i == 0 || j == 0)
dp[i][j] = ULLONG_MAX;
else if(j >= arr[i].second)
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - arr[i - 1].second] + arr[i - 1].first);
else
dp[i][j] = dp[i - 1][j];
}
}
for(int i = 0; i <= n; i++){
for(int j = 0; j <= (max_val * n); j++)
cout << dp[i][j] << " ";
cout << endl;
}
int i;
for(i = (max_val * n); i >= 1; i--){
if(dp[n][i] != ULLONG_MAX && dp[n][i] <= w)
break;
}
cout << i;
return 0;
}
But I am not getting correct answer for all inputs. For example, for input
3 8
3 30
4 50
5 60
I am getting 180 as answer, whereas 90 is the accurate answer. Please help me fix it.

You are using 2-D array when weight can be 10^9,
And the complexity of your code is O(n*w).
So that will give TLE in other cases.
Use unordered_map for memoization.

Related

Range max sum query using sparse table

I Implemented range max sum query using sparse Table ,I Know more efficient approach would be using segment trees.
What I have tried:
I am calculating the max sum in the range (i,2^j-1) for all possible value of i and j and storing them in a table
where i is the index and j denotes the power of 2 (2^j denotes the length of segment from i for which we are calculating max sum)
Now using the above table we can answer the queries
Input:
3
-1 2 3
1
1 2
expected output:
2
actual output:
"wrong answer(garbage value)"
we actually have to tell the max contiguous sum in a given query
Link to the ques spoj gss1
Please help:
#include<iostream>
#include<vector>
#include<algorithm>
#include<climits>
using namespace std;
const int k = 16;
const int N = 1e5;
const int ZERO = 0; // ZERO + x = x + ZERO = x (for any x)
long long table[N][k + 1]; // k + 1 because we need to access table[r][k]
long long Arr[N];
int main()
{
int n, L, R, q;
cin >> n; // array size
for(int i = 0; i < n; i++)
cin >> Arr[i];
// build Sparse Table
for(int i = 0; i < n; i++)
table[i][0] = Arr[i];
for(int j = 1; j <= k; j++) {
for(int i = 0; i <= n - (1 << j); i++)
//table[i][j] = table[i][j - 1] + table[i + (1 << (j - 1))][j - 1];
table[i][j] = max(table[i][j-1],max(table[i+(1<<(j-1))][j-1],table[i+(1<<(j-1))][j-1]+table[i][j-1]));
}
cin >> q; // number of queries
for(int i = 0; i < q; i++) {
cin >> L >> R; // boundaries of next query, 0-indexed
long long int answer = LLONG_MIN;
for(int j = k; j >= 0; j--) {
if(L + (1 << j) - 1 <= R) {
answer = max(answer,answer + table[L][j]);
L += 1 << j; // instead of having L', we increment L directly
}
}
cout << answer << endl;
}
return 0;
}
link to the question Spoj Gss1

Why does this "improvement" to this algoirthm not work?

I created an algorithm to solve the following problem statement:
The problem statement is :
We say that two integers x and y have a variation of at least K, if |x − y| ≥ K (the absolute value of their difference is at least K). Given a sequence of N integers a1,a2,...,aN and K, the total variation count is the number of pairs of elements in the sequence with variation at least K, i.e. it is the size of the set of pairs
{(i,j)|1≤i<j≤N and|ai−aj|≥K}
code:
int n, k, count = 0;
cin >> n >> k;
int v[n];
for (int i = 0; i < n; i++) {
cin >> v[i];
}
sort(v, v + n);
for (int i = 0, j = 1; i < n;) {
if (abs(v[i] - v[j]) >= k) {
count += (n-j);
i++;
}
else
j++;
}
cout << count;
return 0; }
If we have the input:
4 1 // first int is the number of N integers, the second int is the k.
3 1 3 2 // first integers.
So my problem is the following: If I want to improve the algorithm by adding j++ under the i++ in the if block, the output changes from 5 (the correct one) to 4. When i test this improvement on papper and it seems to work, but in code it doesn't, why?
if I trace the execution with your inputs
without the added j++ associated to the i++ :
(abs(v[0] - v[1]) >= k) = 1
count += (n-1)
(abs(v[1] - v[1]) >= k) = 0
(abs(v[1] - v[2]) >= k) = 1
count += (n-2)
(abs(v[2] - v[2]) >= k) = 0
(abs(v[2] - v[3]) >= k) = 0
(abs(v[2] - v[4]) >= k) = 1
count += (n-4)
(abs(v[3] - v[4]) >= k) = 1
count += (n-4)
result 5
if I add j++ in the block containing i++ :
(abs(v[0] - v[1]) >= k) = 1
count += (n-1)
(abs(v[1] - v[2]) >= k) = 1
count += (n-2)
(abs(v[2] - v[3]) >= k) = 0
(abs(v[2] - v[4]) >= k) = 1
count += (n-4)
(abs(v[3] - v[5]) >= k) = 1
count += (n-5)
result 4
This is not the same thing, in that case the result is not the same because of the last count += (n-5) rather than count += (n-4) because of the value of j
[edit add]
The program to test :
#include <iostream>
using namespace std;
int main(int, char **)
{
#if 0
int n, k, count = 0;
cin >> n >> k;
int v[n];
for (int i = 0; i < n; i++) {
cin >> v[i];
}
sort(v, v + n);
#else
int n = 4, k = 1;
int v[] = { 1, 2, 3, 3};
int count = 0;
#endif
for (int i = 0, j = 1; i < n;) {
cout << "(abs(v[" << i << "] - v[" << j << "]) >= k) = " << (abs(v[i] - v[j]) >= k) << endl;
if (abs(v[i] - v[j]) >= k) {
cout << "\tcount += (n-" << j << ")" << endl;
count += (n-j);
j++; // THIS LINE IS IN COMMENT OR OUT OF COMMENT
i++;
}
else
j++;
}
cout << count;
return 0;
}
[/edit]

C++ Sub sequence Exercise

I was assigned a "project" where I should create a C++ program to find the largest possible sum of two sub sequences. The user inputs N (array length), K (length of each sub sequence) and N numbers representing the array. The two sub sequences can touch but can't override each other e.g. 1 5 20 20 20 15 10 1 1 1 should output 90 ((5+20+20)+(20+15_10)) and not 115 ((20+20+20)+(20+20+15)).
My code until now is:
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int N, K, MaxN;
cin >> N;
cin >> K;
int Pi[N];
MaxN = N - K + 1;
int Word[MaxN];
int MaxSum;
for(int nn=0; nn<N; nn++) {
cin >> Pi[nn];
}
for(int y=0;y<MaxN;y++) {
Word[y] = 0;
}
for(int j=0; j<MaxN; j++) {
for(int l=0; l<K; l++) {
Word[j] = Word[j] + Pi[j+l];
}
}
sort(Word, Word + MaxN);
MaxSum = Word[MaxN-2] + Word[MaxN-1];
cout << MaxSum;
return 0;
}
Which is correct only in the case where the 2 sub sequences don't interfere with each other e.g. in an array such as 2 4 15 12 10 1 1 20 4 10 which outputs 71.
Thank you all in advance.
This is solution:
precalculate prefixes and suffixes
iterate end of the first subarray
iterate begin of the second subarray, but start from the end of first sub. ar. + 1
we have sum of numbers on interval from 0 to *end* = prefix[end], but we are interested only in interval [end - k, k], so simply subtract prefix[end] - prefix[end - k - 1]
[0 .. end-k-1, end-k .. end]
The same approach for the second subarray: sum2 = suffix[begin] - suffix[begin + i + 1]
then compare with the previous answer
So we just brute-force all possible sub-arrays which not intersect and find the max their sum
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int N,K,MaxN;
cin >> N;
cin >> K;
int Pi[N];
MaxN=N-K+1;
int Word[MaxN];
int MaxSum;
for(int nn=0;nn<N;nn++){
cin >> Pi[nn];
}
int prefix[N];
int sufix[N];
for (int i = 0; i < N; i++) {
prefix[i] = sufix[i] = 0;
}
for (int i = 0; i < N; i++) {
if (i == 0)
prefix[i] = Pi[i];
else
prefix[i] = Pi[i] + prefix[i - 1];
}
for (int i = N - 1; i >= 0; i--) {
if (i == N - 1)
sufix[i] = Pi[i];
else
sufix[i] = Pi[i] + sufix[i + 1];
}
int ans = 0;
for (int i = K - 1; i < MaxN; i++) {
for (int j = i + 1; j < MaxN; j++) {
int x = prefix[i] - (i - K >= 0 ? prefix[i - K] : 0);
int y = sufix[j] - (j + K < N ? sufix[j + K] : 0);
ans = max(ans, x + y);
}
}
cout << ans;
return 0;
}

Combinatorics - The Candies

I found this problem somewhere in a contest and haven't been able to come up with a solution yet.
The boy has apples and keeps in boxes. In one box no more than N/2.
How many methods he can put candies to boxes.
So what I'm trying to do is to implement solution using DP. Here is my code:
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <unistd.h>
#include <vector>
#define size 1002
using namespace std;
long long a[size][size];
int n, k;
int main()
{
cin >> n >> k;
int kk = n/2;
for(int i = 0; i <= k; ++i)
a[0][i] = 1;
a[0][0] = 0;
for(int i = 0; i <= kk; ++i)
a[i][1] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 2; j <= k; ++j) {
int index = 0;
long long res = 0;
while(1) {
res += a[i-index][j - 1];
index += 1;
if(index == kk + 1 || i-index < 0)
break;
}
a[i][j] = res;
}
}
cout << a[n][k] << endl;
}
But the problem is that we have large numbers in input like:
2 ≤ N ≤ 1000 is a quantity of the candies, N - even; 2 ≤ S ≤ 1000 - is a quantity of small boxes.
So, for input like N = 1000 and S = 1000, I have to spent 5*10^8 operations. And the numbers are very big, so I have to use BigInteger arithmetics?
Maybe there is algorithm to implement the problem in linear time? Thanks and sorry for my English!
You can easily decrease the time complexity from O(kn^2) into O(nk) by the following observation:
for(int i = 1; i <= n; ++i) {
for(int j = 2; j <= k; ++j) {
int index = 0;
long long res = 0;
while(1) {
res += a[i-index][j - 1];
index += 1;
if(index == kk + 1 || i-index < 0)
break;
}
a[i][j] = res;
}
}
for each a[i][j], we can easily see that
a[i][j] = sum a[k][j - 1] with k from (i - n/2) to i
So, if we create an array sum to store the sum from all indexes of the previous step, we can reduce one for loop from the above nested loop
a[i][j] = sum[i] - sum[i - (n/2) - 1];
Pseudo code:
long long sum[n + 1];
for(int j = 2; j <= k; ++j) {
long long nxt[n + 1];
for(int i = 1; i <= n; ++i) {
int index = 0;
long long res = sum[i] - sum[i - (n/2) - 1];
a[i][j] = res;
nxt[i] = nxt[i - 1] + a[i][j];//Prepare the sum array for next step
}
sum = nxt;
}
Note: This above code is not handled the initialization step for array sum, as well as not handle the case when i < n/2. Those cases should be obvious to handle.
Update:
My below Java solution get accepted by using similar idea:
public static void main(String[] args) throws FileNotFoundException {
// PrintWriter out = new PrintWriter(new FileOutputStream(new File(
// "output.txt")));
PrintWriter out = new PrintWriter(System.out);
Scanner in = new Scanner();
int n = in.nextInt();
int s = in.nextInt();
BigInteger[][] dp = new BigInteger[n + 1][2];
BigInteger[][] count = new BigInteger[2][n + 1];
int cur = 1;
for (int i = 0; i <= n / 2; i++) {
dp[i][0] = BigInteger.ONE;
count[0][i] = (i > 0 ? count[0][i - 1] : BigInteger.ZERO)
.add(dp[i][0]);
}
for (int i = n / 2 + 1; i <= n; i++) {
dp[i][0] = BigInteger.ZERO;
count[0][i] = count[0][i - 1];
}
for (int i = 2; i <= s; i++) {
for (int j = 0; j <= n; j++) {
dp[j][cur] = dp[j][1 - cur].add((j > 0 ? count[1 - cur][j - 1]
: BigInteger.ZERO)
.subtract(j > n / 2 ? count[1 - cur][j - (n / 2) - 1]
: BigInteger.ZERO));
count[cur][j] = (j > 0 ? count[cur][j - 1] : BigInteger.ZERO)
.add(dp[j][cur]);
}
cur = 1 - cur;
}
out.println(dp[n][1 - cur]);
out.close();
}

Project Euler 16 2^1000 i got 1319?

i checked the answer and it's not off by much from my answer, but it is still an error. can someone check my coding to see what mistake caused me to get a value of 1319?
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int k = 0;
int o = 0;
vector<int> n(1,0);
n[0] = 1;
while (k < 1001)
{
for (int l = 0; l < n.size(); l++)
n[l] = n[l] * 2;
for (int l = 0; l < n.size(); l++)
{
if (n[l] >= 1000000)
{
int m;
if (l == n.size() - 1)
n.resize(n.size() + 1);
m = n[l] / 1000000;
n[l] = n[l] % 1000000;
n[l+1] = n[l+1] + m;
}
}
k++;
}
for (int l = 0; l < n.size(); l++)
o = o + int (n[l]/1000000) + int ((n[l] % 1000000) / 100000) + int ((n[l] % 100000) / 10000) + int ((n[l] % 10000) / 1000) + int ((n[l] % 1000) / 100) + int ((n[l] % 100) / 10) + n[l] % 10;
cout << o;
cin >> k;
return 0;
}
Make it
while (k < 1000)
in the outer loop condition.
In the while loop, you start with a representation of 2^k in the vector with the value k has at entering the loop. So you are actually computing 2^1001 and not 2^1000.
Problem of solving this in c++ is datatype limit, "int" is not sufficient to calculate 2^1000.
I have solved a prototype for this i.e. sum of the digits of number 2^4.The power 2^4 is 16 and sum of digits id 7.
Hope the code guides u.
#include<iostream.h>
#include<conio.h>
void main()
{
int count=1;
int power=1;
int sum=0;
while(count<=4)
{ count++;
power=power*2;
}
cout<<"The power is"<<power<<"\t";
while(power!=0)
{
int digit=power%10;
sum=sum+digit;
power=power/10;
}
cout<<"The sum of digits is"<<sum;
getch();
}