Every time I change the constant, the result is different - c++

I want to code the 01 Knapsack problem with c++. Each time I change the value of a constant MAX to a different value, such as 100,90, or 80, the result is different. What's the cause? I'm using Visual Studio 2019.
The input is a text file, for example.
20
40 35 18 4 10 2 70 20 39 37 7 5 10 8 15 21 50 40 10 30
100 50 45 20 10 5 31 10 20 19 4 3 6 8 12 7 10 2 5 5
137
The first row is the number of objects, the second row is the price, the third row is the weight, and the last row is the size of the knapsack.
#include <stdio.h>
#include <time.h>
#define MAX 100
#define CLOCKS_PER_MS CLOCKS_PER_SEC/1000
int X_d[MAX] = { 0 }; //solution vector
int max(int a, int b) {
if (a >= b)
return a;
else
return b;
}
void dynamic(int n, int M, int p[], int w[]) {
int result;
int i, y, k;
int P[MAX][MAX] = { 0 };
clock_t start, finish;
start = clock();
for (i = 0; i <= n; i++) {
for (y = 0; y <= M; y++) {
if (i == 0 || y == 0)
P[i][y] = 0;
else if (w[i - 1] > y)
P[i][y] = P[i - 1][y];
else
P[i][y] = max(P[i - 1][y], p[i - 1] + P[i - 1][y - w[i - 1]]);
}
}
finish = clock();
result = P[n][M];
y = M;
for (i = n; i > 0 && result > 0; i--) {
if (result == P[i - 1][y]) {
continue;
}
else {
X_d[i - 1] = 1;
result = result - p[i - 1];
y = y - w[i - 1];
}
}
printf("\n(1) Dynamic Programming");
printf("\nThe maximum profit is $%d", P[n][M]);
printf("\nThe solution vetor X = ( ");
for (k = 0; k < n; k++)
printf("%d ", X_d[k]);
printf(")\n");
printf("The execution time is %f milliseconds.\n", (float)(finish - start) / CLOCKS_PER_MS);
}
int main() {
int i, j;
int num, M;
int p[MAX] = { 0 }, w[MAX] = { 0 };
FILE* fp = NULL;
fopen_s(&fp, "p2data6.txt", "r");
fscanf_s(fp, "%d", &num);
for (i = 0; i < num; i++)
fscanf_s(fp, "%d", &p[i]);
for (i = 0; i < num; i++)
fscanf_s(fp, "%d", &w[i]);
fscanf_s(fp, "%d", &M);
printf("n = %d\n", num);
printf("pi = ");
for (i = 0; i < num; i++)
printf("%3d ", p[i]);
printf("\nwi = ");
for (i = 0; i < num; i++)
printf("%3d ", w[i]);
printf("\npi/wi = ");
for (i = 0; i < num; i++)
printf("%f ", (double)p[i] / w[i]);
printf("\nM = %d\n", M);
dynamic(num, M, p, w);
return 0;
}

Geeks for Geeks has a very good explanation of this problem here:
To quote the Geeks for Geeks article by Sam007:
A simple solution is to consider all subsets of items and calculate the total weight and value of all subsets. Consider the only subsets whose total weight is smaller than W. From all such subsets, pick the maximum value subset.
1) Optimal Substructure:
To consider all subsets of items, there can be two cases for every item: (1) the item is included in the optimal subset, (2) not included in the optimal set.
Therefore, the maximum value that can be obtained from n items is max of following two values.
1) Maximum value obtained by n-1 items and W weight (excluding nth item).
2) Value of nth item plus maximum value obtained by n-1 items and W minus weight of the nth item (including nth item).
If weight of nth item is greater than W, then the nth item cannot be included and case 1> is the only possibility.
2) Overlapping Subproblems
Following is recursive implementation that simply follows the recursive structure mentioned above.

Related

If NxM multiplication table put in order, what is number on K position?

If I have multiplication table 3x4
1 2 3 4
2 4 6 8
3 6 9 12
and put all these numbers in the order:
1 2 2 3 3 4 4 6 6 8 9 12
What number at the K position?
For example, if K = 5, then this is number 3.
N and M in the range 1 to 500 000. K is always less then N * M.
I've tried to use binary-search like in this(If an NxM multiplication table is put in order, what is number in the middle?) solution, but there some mistake if desired value not in the middle of sequence.
long findK(long n, long m, long k)
{
long min = 1;
long max = n * m;
long ans = 0;
long prev_sum = 0;
while (min <= max) {
ans = (min + max) / 2;
long sum = 0;
for (int i = 1; i <= m; i++)
{
sum += std::min(ans / i, n);
}
if (prev_sum + 1 == sum) break;
sum--;
if (sum < k) min = ans - 1;
else if (sum > k) max = ans + 1;
else break;
prev_sum = sum;
}
long sum = 0;
for (int i = 1; i <= m; i++)
sum += std::min((ans - 1) / i, n);
if (sum == k) return ans - 1;
else return ans;
}
For example, when N = 1000, M = 1000, K = 876543; expected value is 546970, but returned 546972.
I believe that the breakthrough will lie with counting the quantity of factorizations of each integer up to the desired point. For each integer prod, you need to count how many simple factorizations i*j there are with i <= m, j <= n. See the divisor functions.
You need to iterate prod until you reach the desired point, midpt = N*M / 2. Cumulatively subtract σ0(prod) from midpt until you reach 0. Note that once prod passes min(i, j), you need to start cropping the divisor count, due to running off the edge of the multiplication table.
Is that enough to get you started?
Code of third method from this(https://leetcode.com/articles/kth-smallest-number-in-multiplication-table/#) site solve the problem.
bool enough(int x, int m, int n, int k) {
int count = 0;
for (int i = 1; i <= m; i++) {
count += std::min(x / i, n);
}
return count >= k;
}
int findK(int m, int n, int k) {
int lo = 1, hi = m * n;
while (lo < hi) {
int mi = lo + (hi - lo) / 2;
if (!enough(mi, m, n, k)) lo = mi + 1;
else hi = mi;
}
return lo;
}

Print Sum of int > 0

Given a number S ( int > 0 ) and n (int > 0), print all the different subsets of len n which sum to S.
For S = 7 and n = 3, the output is the following, the output must be descending order:
5 + 1 + 1
4 + 2 + 1
3 + 3 + 1
3 + 2 + 2
Here is what I've tried so far:
vector<vector<int> > partitions(int X, int Y)
{
vector<vector<int> > v;
if (X <= 1 && X <= X - Y + 1)
{
v.resize(1);
v[0].push_back(X);
return v;
}
for (int y = min(X - 1, Y); y >= 1; y--)
{
vector<vector<int> > w = partitions(X - y, y);
for (int i = 0; i<w.size(); i++)
{
w[i].push_back(y);
v.push_back(w[i]);
}
}
return v;
}
int main()
{
vector<vector<int> > v = partitions(7, 3);
int i;
for (i = 0; i<v.size(); i++)
{
int x;
for (x = 0; x<v[i].size(); x++)
printf("%d ", v[i][x]);
printf("\n");
}
}
the first element in the matrix is s- n + 1 and full of 1 till the sum is reached, or if the s-n+1 is equal to s, then n is 1, so only s will be the solution.
p.s.: I don t know if this problem has a particular name
This may not be the best solution for your problem, since it's not a dynamic programming based solution. In this case, I'm using recursion to fill an array until I reduce the desired number to 0. In this solution, every combination will be stored in the increasing order of the elements so we prevent permutations of a already calculated solution.
#include <iostream>
void findCombinationGivenSize(int numbersArray[], int index, int num, int reducedNum, int maxNum){
if (reducedNum < 0)
return; // this is our base case
if (reducedNum == 0 && index == maxNum){ // both criteria were attended:
//the sum is up to num, and the subset contain maxNum numbers
for (int i = index - 1; i>=0; i--)
std::cout<< numbersArray[i] << " + ";
// here we will have a problem with an extra '+' on the end, but you can figure out easily how to remove it :)
std::cout<<std::endl;
return;
}
// Find the previous number stored in arrayNumber[]
int prev;
if(index == 0)
prev = 1;
else
prev = numbersArray[index-1];
for (int k = prev; k <= num; k++){
// next element of array is k
numbersArray[index] = k;
// call recursively with reduced number
findCombinationGivenSize(numbersArray, index + 1, num,reducedNum - k, maxNum);
}
}
void findCombinations(int number, int maxSubset){
int arrayNumbers[number];
findCombinationGivenSize(arrayNumbers, 0, number, number, maxSubset);
}
int main(){
int number = 7;
int maxPartitions = 3;
findCombinations(number, maxPartitions);
return 0;
}

generate a 2d array of integers from given sums of its rows and columns

I want to generate an array of integers where the total sum of each row and column in the array is known , for example if I create a 4 by 4 array in c++ and then populate it pseudo randomly with numbers between 1 and 100:
int array[4][4] = {} ;
for(int x = 0 ; x<4 ; x++){
for(int y = 0 ; y<4 ; y++){
array[x][y] = rand() % 100 + 1 ;
}
}
the array would be :
8, 50, 74, 59
31, 73, 45, 79
24, 10, 41, 66
93, 43, 88, 4
then if I sum each row and each column by :
int rowSum[4] = {} ;
int columnSum[4] = {} ;
for(int x = 0 ; x < 4; x++){
for(int y = 0 ; y < 4; y++){
rowSum[x] += array[x][y] ;
columnSum[y] += array[x][y] ;
}
}
the rowSum would be {191,228,141,228} and the columnSum = {156,176,248,208}
what I'm trying to do at this point is to generate any random 4x4 1~100 array that will satisfy rowSum and columnSum I understand there is thousands of different arrays that will sum up to the same row and column sum ,and I've been trying to write the part of the code that will generate it , I would really appreciate it if anyone can give me a clue .
It is very easy to find some solution.
Start with generating row that sum to given values. It could be as simple as making all values in each row approximately equal to rowSum[i]/n, give or take one. Of course sums of columns will not match at this point.
Now fix the columns from the leftmost to the rightmost. To fix i th column, distribute the difference between the desired sum and the actual sum equally between column entries, and then fix each row by distributing the added value equally between items i+1...n of the row.
It is easier done than said:
void reconstruct (int array[4][4], int rows[4], int cols[4])
{
// build an array with each row adding up to the correct row sum
for (int x = 0; x < 4; x++){
int s = rows[x];
for(int y = 0; y < 4 ; y++){
array[x][y] = s / (4 - y);
s -= array[x][y];
}
}
// adjust columns
for(int y = 0; y < 4 ; y++){
// calculate the adjustment
int s = 0;
for (int x = 0; x < 4; x++){
s += array[x][y];
}
int diff = s - cols[y];
// adjust the column by diff
for (int x = 0; x < 4; x++){
int k = diff / (4 - x);
array[x][y] -= k;
diff -= k;
// adjust the row by k
for (int yy = y + 1; yy < 4; ++yy)
{
int corr = k / (4 - yy);
array[x][yy] += corr;
k -= corr;
}
}
}
}
This array won't be random of course. One can randomise it by selecting x1, x2, y1, y2 and d at random and executing:
array[x1][y1] += d
array[x1][y2] -= d
array[x2][y1] -= d
array[x2][y2] += d
taking care that the resulting values won't spill out of the desired range.
Here's the quick and dirty brute force search mentioned in comments. It ought to give you a starting point. This is C, not C++.
You never said it, but I'm assuming you want the matrix elements to be non-negative. Consequently, this searches the space where each element a[i][j] can have any value in [0..min(rowsum[i], colsum[j])] with the search cut off when assigning the next array element value would admit no possible future solution.
#include <stdio.h>
int a[4][4] = {
{-1, -1, -1, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1}};
int rs[] = {191, 228, 141, 228};
int cs[] = {156, 176, 248, 208};
long long n_solutions = 0;
void research(int i, int j, int ii, int jj, int val);
void print_a(void);
void search(int i, int j) {
if (j < 3) {
if (i < 3) {
int m = rs[i] < cs[j] ? rs[i] : cs[j];
for (int val = 0; val <= m; ++val) research(i, j, i, j + 1, val);
} else {
if (rs[3] >= cs[j]) research(i, j, i, j + 1, cs[j]);
}
} else {
if (i < 3) {
if (cs[j] >= rs[i]) research(i, 3, i + 1, 0, rs[i]);
} else {
if (rs[3] == cs[3]) {
a[3][3] = rs[i];
if (++n_solutions % 100000000 == 0) {
printf("\n%lld\n", n_solutions);
print_a();
}
a[3][3] = -1;
}
}
}
}
void research(int i, int j, int ii, int jj, int val) {
a[i][j] = val; rs[i] -= val; cs[j] -= val;
search(ii, jj);
rs[i] += val; cs[j] += val; a[i][j] = -1;
}
void print_a(void) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j)
printf("%4d", a[i][j]);
printf("\n");
}
}
int main(void) {
search(0, 0);
printf("Total solutions: %lld\n", n_solutions);
return 0;
}
For example, if you replace the simple for loop with this, you won't get so many zeros in the upper left hand corner:
int b = m / 2; // m/2 can be replaced with any int in [0..m], e.g. a random value.
research(i, j, i, j + 1, b);
for (int d = 1; b + d <= m || b - d >= 0; ++d) {
if (b + d <= m) research(i, j, i, j + 1, b + d);
if (b - d >= 0) research(i, j, i, j + 1, b - d);
}
Here's the 2-billionth solution:
78 56 28 29
39 20 84 85
28 34 61 18
11 66 75 76
The problem becomes interesting if we place condition that the matrix elements must be non-negative integers. Here's an O(mn) JAVA solution based on greedy algorithm.
int m=rowSum.length;
int n=colSum.length;
int mat[][] = new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
int tmp=Math.min(rowSum[i],colSum[j]);
mat[i][j]=tmp;
rowSum[i]-=tmp;
colSum[j]-=tmp;
}
}
return mat;

Codility MinAbsSum

I tried this Codility test: MinAbsSum.
https://codility.com/programmers/lessons/17-dynamic_programming/min_abs_sum/
I solved the problem by searching the whole tree of possibilities. The results were OK, however, my solution failed due to timeout for large input. In other words the time complexity was not as good as expected. My solution is O(nlogn), something normal with trees. But this coding test was in the section "Dynamic Programming", and there must be some way to improve it. I tried with summing the whole set first and then using this information, but always there is something missing in my solution. Does anybody have an idea on how to improve my solution using DP?
#include <vector>
using namespace std;
int sum(vector<int>& A, size_t i, int s)
{
if (i == A.size())
return s;
int tmpl = s + A[i];
int tmpr = s - A[i];
return min (abs(sum(A, i+1, tmpl)), abs(sum(A, i+1, tmpr)));
}
int solution(vector<int> &A) {
return sum(A, 0, 0);
}
I could not solve it. But here's the official answer.
Quoting it:
Notice that the range of numbers is quite small (maximum 100). Hence,
there must be a lot of duplicated numbers. Let count[i] denote the
number of occurrences of the value i. We can process all occurrences
of the same value at once. First we calculate values count[i] Then we
create array dp such that:
dp[j] = −1 if we cannot get the sum j,
dp[j] >= ­ 0 if we can get sum j.
Initially, dp[j] = -1 for all of j (except dp[0] = 0). Then we scan
through all the values a appearing in A; we consider all a such
that count[a]>0. For every such a we update dp that dp[j] denotes
how many values a remain (maximally) after achieving sum j. Note
that if the previous value at dp[j] >= 0 then we can set dp[j] =
count[a] as no value a is needed to obtain the sum j. Otherwise we
must obtain sum j-a first and then use a number a to get sum j. In
such a situation dp[j] = dp[j-a]-1. Using this algorithm, we can
mark all the sum values and choose the best one (closest to half of S,
the sum of abs of A).
def MinAbsSum(A):
N = len(A)
M = 0
for i in range(N):
A[i] = abs(A[i])
M = max(A[i], M)
S = sum(A)
count = [0] * (M + 1)
for i in range(N):
count[A[i]] += 1
dp = [-1] * (S + 1)
dp[0] = 0
for a in range(1, M + 1):
if count[a] > 0:
for j in range(S):
if dp[j] >= 0:
dp[j] = count[a]
elif (j >= a and dp[j - a] > 0):
dp[j] = dp[j - a] - 1
result = S
for i in range(S // 2 + 1):
if dp[i] >= 0:
result = min(result, S - 2 * i)
return result
(note that since the final iteration only considers sums up until S // 2 + 1, we can save some space and time by only creating a DP Cache up until that value as well)
The Java answer provided by fladam returns wrong result for input [2, 3, 2, 2, 3], although it gets 100% score.
Java Solution
import java.util.Arrays;
public class MinAbsSum{
static int[] dp;
public static void main(String args[]) {
int[] array = {1, 5, 2, -2};
System.out.println(findMinAbsSum(array));
}
public static int findMinAbsSum(int[] A) {
int arrayLength = A.length;
int M = 0;
for (int i = 0; i < arrayLength; i++) {
A[i] = Math.abs(A[i]);
M = Math.max(A[i], M);
}
int S = sum(A);
dp = new int[S + 1];
int[] count = new int[M + 1];
for (int i = 0; i < arrayLength; i++) {
count[A[i]] += 1;
}
Arrays.fill(dp, -1);
dp[0] = 0;
for (int i = 1; i < M + 1; i++) {
if (count[i] > 0) {
for(int j = 0; j < S; j++) {
if (dp[j] >= 0) {
dp[j] = count[i];
} else if (j >= i && dp[j - i] > 0) {
dp[j] = dp[j - i] - 1;
}
}
}
}
int result = S;
for (int i = 0; i < Math.floor(S / 2) + 1; i++) {
if (dp[i] >= 0) {
result = Math.min(result, S - 2 * i);
}
}
return result;
}
public static int sum(int[] array) {
int sum = 0;
for(int i : array) {
sum += i;
}
return sum;
}
}
I invented another solution, better than the previous one. I do not use recursion any more.
This solution works OK (all logical tests passed), and also passed some of the performance tests, but not all. How else can I improve it?
#include <vector>
#include <set>
using namespace std;
int solution(vector<int> &A) {
if (A.size() == 0) return 0;
set<int> sums, tmpSums;
sums.insert(abs(A[0]));
for (auto it = begin(A) + 1; it != end(A); ++it)
{
for (auto s : sums)
{
tmpSums.insert(abs(s + abs(*it)));
tmpSums.insert(abs(s - abs(*it)));
}
sums = tmpSums;
tmpSums.clear();
}
return *sums.begin();
}
This solution (in Java) scored 100% for both (correctness and performance)
public int solution(int[] a){
if (a.length == 0) return 0;
if (a.length == 1) return a[0];
int sum = 0;
for (int i=0;i<a.length;i++){
sum += Math.abs(a[i]);
}
int[] indices = new int[a.length];
indices[0] = 0;
int half = sum/2;
int localSum = Math.abs(a[0]);
int minLocalSum = Integer.MAX_VALUE;
int placeIndex = 1;
for (int i=1;i<a.length;i++){
if (localSum<half){
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
localSum += Math.abs(a[i]);
indices[placeIndex++] = i;
}else{
if (localSum == half)
return Math.abs(2*half - sum);
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
if (placeIndex > 1) {
localSum -= Math.abs(a[indices[placeIndex--]]);
i = indices[placeIndex];
}
}
}
return (Math.abs(2*minLocalSum - sum));
}
this solution treats all elements like they are positive numbers and it's looking to reach as close as it can to the sum of all elements divided by 2 (in that case we know that the sum of all other elements will be the same delta far from the half too -> abs sum will be minimum possible ).
it does so by starting with the first element and successively adding others to the "local" sum (and recording indices of elements in the sum) until it reaches sum of x >= sumAll/2. if that x is equal to sumAll/2 we have an optimal solution. if not, we go step back in the indices array and continue picking other element where last iteration in that position ended. the result will be a "local" sum having abs((sumAll - sum) - sum) closest to 0;
fixed solution:
public static int solution(int[] a){
if (a.length == 0) return 0;
if (a.length == 1) return a[0];
int sum = 0;
for (int i=0;i<a.length;i++) {
a[i] = Math.abs(a[i]);
sum += a[i];
}
Arrays.sort(a);
int[] arr = a;
int[] arrRev = new int[arr.length];
int minRes = Integer.MAX_VALUE;
for (int t=0;t<=4;t++) {
arr = fold(arr);
int res1 = findSum(arr, sum);
if (res1 < minRes) minRes = res1;
rev(arr, arrRev);
int res2 = findSum(arrRev, sum);
if (res2 < minRes) minRes = res2;
arrRev = fold(arrRev);
int res3 = findSum(arrRev, sum);
if (res3 < minRes) minRes = res3;
}
return minRes;
}
private static void rev(int[] arr, int[] arrRev){
for (int i = 0; i < arrRev.length; i++) {
arrRev[i] = arr[arr.length - 1 - i];
}
}
private static int[] fold(int[] a){
int[] arr = new int[a.length];
for (int i=0;a.length/2+i/2 < a.length && a.length/2-i/2-1 >= 0;i+=2){
arr[i] = a[a.length/2+i/2];
arr[i+1] = a[a.length/2-i/2-1];
}
if (a.length % 2 > 0) arr[a.length-1] = a[a.length-1];
else{
arr[a.length-2] = a[0];
arr[a.length-1] = a[a.length-1];
}
return arr;
}
private static int findSum(int[] arr, int sum){
int[] indices = new int[arr.length];
indices[0] = 0;
double half = Double.valueOf(sum)/2;
int localSum = Math.abs(arr[0]);
int minLocalSum = Integer.MAX_VALUE;
int placeIndex = 1;
for (int i=1;i<arr.length;i++){
if (localSum == half)
return 2*localSum - sum;
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
if (localSum<half){
localSum += Math.abs(arr[i]);
indices[placeIndex++] = i;
}else{
if (placeIndex > 1) {
localSum -= Math.abs(arr[indices[--placeIndex]]);
i = indices[placeIndex];
}
}
}
return Math.abs(2*minLocalSum - sum);
}
The following is a rendering of the official answer in C++ (scoring 100% in task, correctness, and performance):
#include <cmath>
#include <algorithm>
#include <numeric>
using namespace std;
int solution(vector<int> &A) {
// write your code in C++14 (g++ 6.2.0)
const int N = A.size();
int M = 0;
for (int i=0; i<N; i++) {
A[i] = abs(A[i]);
M = max(M, A[i]);
}
int S = accumulate(A.begin(), A.end(), 0);
vector<int> counts(M+1, 0);
for (int i=0; i<N; i++) {
counts[A[i]]++;
}
vector<int> dp(S+1, -1);
dp[0] = 0;
for (int a=1; a<M+1; a++) {
if (counts[a] > 0) {
for (int j=0; j<S; j++) {
if (dp[j] >= 0) {
dp[j] = counts[a];
} else if ((j >= a) && (dp[j-a] > 0)) {
dp[j] = dp[j-a]-1;
}
}
}
}
int result = S;
for (int i =0; i<(S/2+1); i++) {
if (dp[i] >= 0) {
result = min(result, S-2*i);
}
}
return result;
}
You are almost 90% to the actual solution. It seems you understand recursion very well. Now, You should apply dynamic programming here with your program.
Dynamic Programming is nothing but memoization to the recursion so that we will not calculate same sub problems again and again. If same sub problems encounter , we return the previously calculated and memorized value. Memorization can be done with the help of a 2D array , say dp[][], where first state represent current index of array and second state represent summation.
For this problem specific, instead of giving calls to both states from each state, you sometimes can greedily take decision to skip one call.
I would like to provide the algorithm and then my implementation in C++. Idea is more or less the same as the official codility solution with some constant optimisation added.
Calculate the maximum absolute element of the inputs.
Calculate the absolute sum of the inputs.
Count the number of occurrence of each number in the inputs. Store the results in a vector hash.
Go through each input.
For each input, goes through all possible sums of any number of inputs. It is a slight constant optimisation to go only up to half of the possible sums.
For each sum that has been made before, set the occurrence count of the current input.
Check for each potential sum equal to or greater than the current input whether this input has already been used before. Update the values at the current sum accordingly. We do not need to check for potential sums less than the current input in this iteration, since it is evident that it has not been used before.
The above nested loop will fill in each possible sum with a value greater than -1.
Go through this possible sum hash again to look for the closest sum to half that is possible to make. Eventually, the min abs sum will be the difference of this from the half multiplied by two as the difference will be added up in both groups as the difference from the median.
The runtime complexity of this algorithm is O(N * max(abs(A)) ^ 2), or simply O(N * M ^ 2). That is because the outer loop is iterating M times and the inner loop is iterating sum times. The sum is basically N * M in worst case. Therefore, it is O(M * N * M).
The space complexity of this solution is O(N * M) because we allocate a hash of N items for the counts and a hash of S items for the sums. S is N * M again.
int solution(vector<int> &A)
{
int M = 0, S = 0;
for (const int e : A) { M = max(abs(e), M); S += abs(e); }
vector<int> counts(M + 1, 0);
for (const int e : A) { ++counts[abs(e)]; }
vector<int> sums(S + 1, -1);
sums[0] = 0;
for (int ci = 1; ci < counts.size(); ++ci) {
if (!counts[ci]) continue;
for (int si = 0; si < S / 2 + 1; ++si) {
if (sums[si] >= 0) sums[si] = counts[ci];
else if (si >= ci and sums[si - ci] > 0) sums[si] = sums[si - ci] - 1;
}
}
int min_abs_sum = S;
for (int i = S / 2; i >= 0; --i) if (sums[i] >= 0) return S - 2 * i;
return min_abs_sum;
}
Let me add my 50 cent, how to come up with the score 100% solution.
For me it was hard to understand the ultimate solution, proposed earlier in this thread.
So I started with warm-up solution with score 63%, because its O(NxNxM),
and because it doesn't use the fact that M is quite small value, and there are many duplicates in big arrays
here the key part is to understand how array isSumPossible is filled and interpreted:
how to fill array isSumPossible using numbers in input array:
if isSumPossible[sum] >= 0, i.e. sum is already possible, even without current number, then let's set it's value to 1 - count of current number, that is left unused for this sum, it'll go to our "reserve", so we can use it later for greater sums.
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
}
if isSumPossible[sum] <= 0, i.e. sum is considered not yet possible, with all input numbers considered previously, then let's check maybe
smaller sum sum - number is already considered as possible, and we have in "reserve" our current number (isSumPossible[sum - number] == 1), then do following
else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
here isSumPossible[sum] = 0 means that we have used number in composing sum and it's now considered as possible (>=0), but we have no number in "reserve", because we've used it ( =0)
how to interpret filled array isSumPossible after considering all numbers in input array:
if isSumPossible[sum] >= 0 then the sum is possible, i.e. it can be reached by summation of some numbers in given array
if isSumPossible[sum] < 0 then the sum can't be reached by summation of any numbers in given array
The more simple thing here is to understand why we are searching sums only in interval [0, maxSum/2]:
because if find a possible sum, that is very close to maxSum/2,
ideal case here if we've found possible sum = maxSum/2,
if so, then it's obvious, that we can somehow use the rest numbers in input array to make another maxSum/2, but now with negative sign, so as a result of annihilation we'll get solution = 0, because maxSum/2 + (-1)maxSum/2 = 0.
But 0 the best case solution, not always reachable.
But we, nevertheless, should seek for the minimal delta = ((maxSum - sum) - sum),
so this we seek for delta -> 0, that's why we have this:
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, (maxSum - sum) - sum);
}
}
warm-up solution
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxSum += A[i];
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number : A) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
} else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
and after this we can optimize it, using the fact that there are many duplicate numbers in big arrays, and we come up with the solution with 100% score, its O(Mx(NxM)), because maxSum = NxM at worst case
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxNumber = 0;
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxNumber = Math.max(maxNumber, A[i]);
maxSum += A[i];
}
int[] count = new int[maxNumber + 1];
for (int i = 0; i < A.length; i++) {
count[A[i]]++;
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number = 0; number < maxNumber + 1; number++) {
if (count[number] > 0) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = count[number];
} else if (sum >= number && isSumPossible[sum - number] > 0) {
isSumPossible[sum] = isSumPossible[sum - number] - 1;
}
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
I hope I've made it at least a little clear
Kotlin solution
Time complexity: O(N * max(abs(A))**2)
Score: 100%
import kotlin.math.*
fun solution(A: IntArray): Int {
val N = A.size
var M = 0
for (i in 0 until N) {
A[i] = abs(A[i])
M = max(M, A[i])
}
val S = A.sum()
val counts = MutableList(M + 1) { 0 }
for (i in 0 until N) {
counts[A[i]]++
}
val dp = MutableList(S + 1) { -1 }
dp[0] = 0
for (a in 1 until M + 1) {
if (counts[a] > 0) {
for (j in 0 until S) {
if (dp[j] >= 0) {
dp[j] = counts[a]
} else if (j >= a && dp[j - a] > 0) {
dp[j] = dp[j - a] - 1
}
}
}
}
var result = S
for (i in 0 until (S / 2 + 1)) {
if (dp[i] >= 0) {
result = minOf(result, S - 2 * i)
}
}
return result
}

C++ Newbie needs helps for printing combinations of integers

Suppose I am given:
A range of integers iRange (i.e. from 1 up to iRange) and
A desired number of combinations
I want to find the number of all possible combinations and print out all these combinations.
For example:
Given: iRange = 5 and n = 3
Then the number of combinations is iRange! / ((iRange!-n!)*n!) = 5! / (5-3)! * 3! = 10 combinations, and the output is:
123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345
Another example:
Given: iRange = 4 and n = 2
Then the number of combinations is iRange! / ((iRange!-n!)*n!) = 4! / (4-2)! * 2! = 6 combinations, and the output is:
12 - 13 - 14 - 23 - 24 - 34
My attempt so far is:
#include <iostream>
using namespace std;
int iRange= 0;
int iN=0;
int fact(int n)
{
if ( n<1)
return 1;
else
return fact(n-1)*n;
}
void print_combinations(int n, int iMxM)
{
int iBigSetFact=fact(iMxM);
int iDiffFact=fact(iMxM-n);
int iSmallSetFact=fact(n);
int iNoTotComb = (iBigSetFact/(iDiffFact*iSmallSetFact));
cout<<"The number of possible combinations is: "<<iNoTotComb<<endl;
cout<<" and these combinations are the following: "<<endl;
int i, j, k;
for (i = 0; i < iMxM - 1; i++)
{
for (j = i + 1; j < iMxM ; j++)
{
//for (k = j + 1; k < iMxM; k++)
cout<<i+1<<j+1<<endl;
}
}
}
int main()
{
cout<<"Please give the range (max) within which the combinations are to be found: "<<endl;
cin>>iRange;
cout<<"Please give the desired number of combinations: "<<endl;
cin>>iN;
print_combinations(iN,iRange);
return 0;
}
My problem:
The part of my code related to the printing of the combinations works only for n = 2, iRange = 4 and I can't make it work in general, i.e., for any n and iRange.
Your solution will only ever work for n=2. Think about using an array (combs) with n ints, then the loop will tick up the last item in the array. When that item reaches max update then comb[n-2] item and set the last item to the previous value +1.
Basically working like a clock but you need logic to find what to uptick and what the next minimum value is.
Looks like a good problem for recursion.
Define a function f(prefix, iMin, iMax, n), that prints all combinations of n digits in the range [iMin, iMax] and returns the total number of combinations. For n = 1, it should print every digit from iMin to iMax and return iMax - iMin + 1.
For your iRange = 5 and n = 3 case, you call f("", 1, 5, 3). The output should be 123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345.
Notice that the first group of outputs are simply 1 prefixed onto the outputs of f("", 2, 5, 2), i.e. f("1", 2, 5, 2), followed by f("2", 3, 5, 2) and f("3", 4, 5, 2). See how you would do that with a loop. Between this, the case for n = 1 above, and traps for bad inputs (best if they print nothing and return 0, it should simplify your loop), you should be able to write f().
I'm stopping short because this looks like a homework assignment. Is this enough to get you started?
EDIT: Just for giggles, I wrote a Python version. Python has an easier time throwing around sets and lists of things and staying legible.
#!/usr/bin/env python
def Combos(items, n):
if n <= 0 or len(items) == 0:
return []
if n == 1:
return [[x] for x in items]
result = []
for k in range(len(items) - n + 1):
for s in Combos(items[k+1:], n - 1):
result.append([items[k]] + s)
return result
comb = Combos([str(x) for x in range(1, 6)], 3)
print len(comb), " - ".join(["".join(c) for c in comb])
Note that Combos() doesn't care about the types of the items in the items list.
Here is your code edited :D :D with a recursive solution:
#include <iostream>
int iRange=0;
int iN=0; //Number of items taken from iRange, for which u want to print out the combinations
int iTotalCombs=0;
int* pTheRange;
int* pTempRange;
int find_factorial(int n)
{
if ( n<1)
return 1;
else
return find_factorial(n-1)*n;
}
//--->Here is another solution:
void print_out_combinations(int *P, int K, int n_i)
{
if (K == 0)
{
for (int j =iN;j>0;j--)
std::cout<<P[j]<<" ";
std::cout<<std::endl;
}
else
for (int i = n_i; i < iRange; i++)
{
P[K] = pTheRange[i];
print_out_combinations(P, K-1, i+1);
}
}
//Here ends the solution...
int main()
{
std::cout<<"Give the set of items -iRange- = ";
std::cin>>iRange;
std::cout<<"Give the items # -iN- of iRange for which the combinations will be created = ";
std::cin>>iN;
pTheRange = new int[iRange];
for (int i = 0;i<iRange;i++)
{
pTheRange[i]=i+1;
}
pTempRange = new int[iN];
iTotalCombs = (find_factorial(iRange)/(find_factorial(iRange-iN)*find_factorial(iN)));
std::cout<<"The number of possible combinations is: "<<iTotalCombs<<std::endl;
std::cout<<"i.e.the combinations of "<<iN<<" elements drawn from a set of size "<<iRange<<" are: "<<std::endl;
print_out_combinations(pTempRange, iN, 0);
return 0;
}
Here's an example of a plain recursive solution. I believe there exists a more optimal implementation if you replace recursion with cycles. It could be your homework :)
#include <stdio.h>
const int iRange = 9;
const int n = 4;
// A more efficient way to calculate binomial coefficient, in my opinion
int Cnm(int n, int m)
{
int i;
int result = 1;
for (i = m + 1; i <= n; ++i)
result *= i;
for (i = n - m; i > 1; --i)
result /= i;
return result;
}
print_digits(int *digits)
{
int i;
for (i = 0; i < n; ++i) {
printf("%d", digits[i]);
}
printf("\n");
}
void plus_one(int *digits, int index)
{
int i;
// Increment current digit
++digits[index];
// If it is the leftmost digit, run to the right, setup all the others
if (index == 0) {
for (i = 1; i < n; ++i)
digits[i] = digits[i-1] + 1;
}
// step back by one digit recursively
else if (digits[index] > iRange) {
plus_one(digits, index - 1);
}
// otherwise run to the right, setting up other digits, and break the recursion once a digit exceeds iRange
else {
for (i = index + 1; i < n; ++i) {
digits[i] = digits[i-1] + 1;
if (digits[i] > iRange) {
plus_one(digits, i - 1);
break;
}
}
}
}
int main()
{
int i;
int digits[n];
for (i = 0; i < n; ++i) {
digits[i] = i + 1;
}
printf("%d\n\n", Cnm(iRange, n));
// *** This loop has been updated ***
while (digits[0] <= iRange - n + 1) {
print_digits(digits);
plus_one(digits, n - 1);
}
return 0;
}
This is my C++ function with different interface (based on sts::set) but performing the same task:
typedef std::set<int> NumbersSet;
typedef std::set<NumbersSet> CombinationsSet;
CombinationsSet MakeCombinations(const NumbersSet& numbers, int count)
{
CombinationsSet result;
if (!count) throw std::exception();
if (count == numbers.size())
{
result.insert(NumbersSet(numbers.begin(), numbers.end()));
return result;
}
// combinations with 1 element
if (!(count - 1) || (numbers.size() <= 1))
{
for (auto number = numbers.begin(); number != numbers.end(); ++number)
{
NumbersSet single_combination;
single_combination.insert(*number);
result.insert(single_combination);
}
return result;
}
// Combinations with (count - 1) without current number
int first_num = *numbers.begin();
NumbersSet truncated_numbers = numbers;
truncated_numbers.erase(first_num);
CombinationsSet subcombinations = MakeCombinations(truncated_numbers, count - 1);
for (auto subcombination = subcombinations.begin(); subcombination != subcombinations.end(); ++subcombination)
{
NumbersSet cmb = *subcombination;
// Add current number
cmb.insert(first_num);
result.insert(cmb);
}
// Combinations with (count) without current number
subcombinations = MakeCombinations(truncated_numbers, count);
result.insert(subcombinations.begin(), subcombinations.end());
return result;
}
I created a next_combination() function similar to next_permutation(), but valid input is required to make it work
//nums should always be in ascending order
vector <int> next_combination(vector<int>nums, int max){
int size = nums.size();
if(nums[size-1]+1<=max){
nums[size-1]++;
return nums;
}else{
if(nums[0] == max - (size -1)){
nums[0] = -1;
return nums;
}
int pos;
int negate = -1;
for(int i = size-2; i>=0; i--){
if(nums[i]+1 <= max + negate){
pos = i;
break;
}
negate --;
}
nums[pos]++;
pos++;
while(pos<size){
nums[pos] = nums[pos-1]+1;
pos++;
}
}
return nums;
}