I have an array of numbers say [1,2,3,1,1000] , now I want to get all possible combinations of this array and calculate its sum. Combinations are valid such that two combinations have different subset of elements. Then order all the sum values in descending order and get the top k elements.
Example:
[1,2,3,1,1000]
Combinations:
Duplicates of earlier ones are striked out, for example (3,1) matches the earlier (1,3).
(), (1), (2), (3), (1), (1000), (1,2), (1,3), (1,1), (1,1000), (2,3), (2,1), (2,1000), (3,1), (3,1000), (1,1000), (1,2,3), (1,2,1), (1,2,1000), (1,3,1), (1,3,1000), (1,1,1000), (2,3,1), (2,3,1000), (2,1,1000), (3,1,1000), (1,2,3,1), (1,2,3,1000), (1,2,1,1000), (1,3,1,1000), (2,3,1,1000), (1,2,3,1,1000)
And the corresponding sums:
0, 1, 2, 3, 1, 1000, 3, 4, 2, 1001, 5, 3, 1002, 4, 1003, 1001, 6, 4, 1003, 5, 1004, 1002, 6, 1005, 1003, 1004, 7, 1006, 1004, 1005, 1006, 1007
Getting top k=3, sums = 1007, 1006, 1005
So output is [1007, 1006, 1005].
Constraints:
Array size n = 1 to 105
Array elements -109 to 109
k ranges from 1 to 2000
This is my code, reference taken from here:
static List<Long> printDistSum(int arr[]) {
List<Long> list = new ArrayList<>();
int n = arr.length;
// There are totoal 2^n subsets
long total = (long) Math.pow(2, n);
// Consider all numbers from 0 to 2^n - 1
for (int i = 0; i < total; i++) {
long sum = 0;
// Consider binary representation of
// current i to decide which elements
// to pick.
for (int j = 0; j < n; j++)
if ((i & (1 << j)) != 0)
sum += arr[j];
// Print sum of picked elements.
list.add(sum);
}
return list;
}
This code works for small range of inputs but times out for large range of inputs. How to solve this program.
I probably have solution that should be good enough. It has time complexity O(n * k * log(k)).
First we need to calculate max sum - sum of all positive values.
Next we need to iterate over positive values, from smallest to largest. For each of these values we calculate sums of new combinations (at the start we have one combination with max sum).
New combinations will not contains given value so we need to substract it from sum.
At the end we need to iterate over negative values. These values are not belongs to combinations from previous step so we need to add these values to sums.
In every iteration are needed only k maximum sums. I used the PriorityQueue to store these sums. That class use heap data structure so adding/removing values has logarithmic time.
Code:
private static long[] findSums(int[] array, int k) {
long maxSum = Arrays.stream(array).filter(it -> it >= 0).sum();
int[] positives = Arrays.stream(array).filter(it -> it >= 0).sorted().toArray();
int[] negatives = Arrays.stream(array).filter(it -> it < 0).sorted().toArray();
// sort time complexity is O(n*log(n))
PriorityQueue<Long> sums = new PriorityQueue<>(k); // priority queue is implemented using heap so adding element has time complexity O(log(n))
sums.add(maxSum); // we start with max sum - combination of all positive elements
int previous = Integer.MIN_VALUE;
Long[] previousAddedSums = {};
Long[] sumsToIterate;
// iterate over positive values
for (int i = 0; i < positives.length; i++) {
if (positives[i] == previous) {
sumsToIterate = previousAddedSums;
} else {
sumsToIterate = sums.toArray(new Long[sums.size()]);
}
previousAddedSums = new Long[sumsToIterate.length];
for (int j = 0; j < sumsToIterate.length; j++) {
long newSum = sumsToIterate[j] - positives[i];
// new sum is calculated - value positives[i] is removed from combination (subtracted from sum of that combination)
sums.add(newSum);
previousAddedSums[j] = newSum;
if (sums.size() > k) {
sums.poll(); // only first k maximum sums are needed at the moment
}
}
previous = positives[i];
}
previous = Integer.MAX_VALUE;
// iterate over negative values in reverse order
for (int i = negatives.length - 1; i >= 0; i--) {
if (negatives[i] == previous) {
sumsToIterate = previousAddedSums;
} else {
sumsToIterate = sums.toArray(new Long[sums.size()]);
}
previousAddedSums = new Long[sumsToIterate.length];
for (int j = 0; j < sumsToIterate.length; j++) {
long newSum = sumsToIterate[j] + negatives[i]; // value negatives[i] is added to combination (added to sum of that combination)
sums.add(newSum);
previousAddedSums[j] = newSum;
if (sums.size() > k) {
sums.poll();
}
}
previous = negatives[i];
}
long[] result = new long[sums.size()];
for (int i = sums.size() - 1; i >=0 ; i--) {
result[i] = sums.poll();
}
// get sums from priority queue in proper order
return result;
// this whole method has time complexity O(n * k * log(k))
// k is less than or equal 2000 so it should be good enough ;)
}
Demo: https://ideone.com/yf6POI
Edit: I have fixed my solution. Instead of iterating over distinct values I check if current value is same like previous. In that case I use combinations (sums) created in previous step. This prevents from creating duplicates of combinations.
I'm sorry if I didn't explain this well enough. I don't have experience in describing algorithmic / mathematical things in english.
Pls ignore all previous posts cuz they are all wrong.
Intuitively, we gotta use backtrack to find all desired combos, but it's impossible to backtrack on 10^5 elements.
Constraint 1 <= n <= 10^5 alludes that our algorithm bottlenecked by O(nlogn) sorting
Constraint 1 <= k <= min(2000,2^n) alludes that we can backtrack on k elements since k is less than 11. 2^11=2024/log(2000)=11 -- actually this "2^n" gives away solution :)
My algorithm (nlog(n) + 2^k)
sort the array
Record the highest score combo which is the sum of all positive integers
Find a window in the sorted array of math.min(log(k)--which is less than 11,n) elements -- worst case, this window consists of the
lowest 11 absolute values in the sorted array. Several approaches to
achieve that, since the candidates must be inside 22 elements
window(11 smallest positive values + 11 biggest negative values), we
can use PriorityQueue of size 11 scanning over these 22 elements. or
we can use two pointers to find the sliding window of size 11.
backtrack on this 11 absolute value elements window, find sum of each combo and put them into a size k/k-1 PriorityQueue. (k is for
the case that there's no positive elements)
result is the sum of all positive integers plus (sum deducted by each of k-1 elements in PriorityQueue).
I was also asked the same question yesterday but sadly I was not able to solve it yesterday. I have tried solving it today and think I have the answer today.
First of all I don't think that different subsets mean different costs in a set i.e in array of [1,2,3,1] both subsets are valid => [1,2,3] and [2,3,1] as they both use different 1's. Now here is my solution keeping this in mind. But if you really want to keep distinct elements in set then you can simply remove the multiple elements and do partial_sort then.
Logic
Store sum of all +ve nos. in a variable, say maxsum.
Convert the negative nos. to their absolute values.
Get lowest min(k-1, n) elements in sorted order.
Find all their combinations and subtract them from the maxsum.
While finding all their combinations we only need lowest k-1 combos. So we have to find a way to keep the number of combinations to that. For that use a sorted data structure and limit its size to k and then for every element in the sorted array iterate through the combos and add those combos to the sorted data structure if the end element of that data structure is greater. Also pop the end element after that.
For taking care of the above point I am using 2 vectors since the order already remains sorted.
The proposed solution has time complexity of O(n*log(k) + k^2).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int ll;
template <class T>
void print(vector<T> topSumm)
{
for (ll itr : topSumm)
cout << itr << '\t';
cout << '\n';
}
vector<ll> mergeSortedArrays(vector<ll> &minns, vector<ll> &temp)
{
vector<ll> ans(minns.size() + temp.size());
int i{0}, j{0}, k{0};
while (i < minns.size() && j < temp.size())
{
if (temp[j] < minns[i])
ans[k++] = temp[j++];
else
ans[k++] = minns[i++];
}
while (i < minns.size())
ans[k++] = minns[i++];
while (j < temp.size())
ans[k++] = temp[j++];
return ans;
}
vector<ll> topKSum(vector<int> &arr, int k)
{
int n{(int)arr.size()};
ll maxSumm{0};
for (int i{0}; i < n; ++i)
{
if (arr[i] > 0)
maxSumm += arr[i];
else
arr[i] = -arr[i];
}
int nk{min(k - 1, n)};
partial_sort(arr.begin(), arr.begin() + nk, arr.end());
vector<ll> minns{0, maxSumm};
ll summ{};
bool breakOuter{false};
for (int i{0}; i < nk; ++i)
{
vector<ll> temp;
for (ll nums : minns)
{
summ = nums + arr[i];
if (minns.size() + temp.size() < k)
temp.push_back(summ);
else
{
if (minns.back() > summ)
{
minns.pop_back();
temp.push_back(summ);
}
else
{
if (nums == 0)
breakOuter = true;
break;
}
}
}
if (breakOuter)
break;
minns = mergeSortedArrays(minns, temp);
}
vector<ll> ans(k);
int i{0};
for (ll nums : minns)
ans[i++] = maxSumm - nums;
return ans;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n, k;
cin >> n >> k;
vector<int> arr(n);
ll maxSumm{0};
for (int i{0}; i < n; ++i)
cin >> arr[i];
vector<ll> topSums = topKSum(arr, k);
print<ll>(topSums);
}
return 0;
}
I have n inputs.
these inputs are numbers from 1 to 100.
I want to output the number that appears less than the other ones; also if there are two numbers with the same amount of appearance, I want to output the number that is less than the other one.
I wrote this code but it doesn't work!
Scanner scanner = new Scanner(System.in);
int n=scanner.nextInt(), max=0 , ans=-1;
int[] counter = new int[n];
for(int i=0; i<n; i++)
counter[scanner.nextInt()]+=1;
for(int j=1; j<=100; j++){
if(counter[j]>max)
max=counter[j];
}
for (int i=1; i<=max; i++){
if(counter[i]>0)
if(ans==-1 || counter[ans]>counter[i] || (counter[ans] == counter[i] && i<ans))
ans=i;
}
System.out.print(ans);
There’s a couple of problems with your code, but the main one is the last for loop: You are trying to find the first (ie lowest) number whose counter is equal to max, so your loop should be from 1 to n, not 1 to max.
Another problem is if you are using the number, which is in the range 1-n, as your array index, you need an array of size n+1, not n.
I pinched this from another question regarding the title of yours:
i = input.nextInt (); while (i != 0) { counts [i]++; i = input.nextInt (); } That method increments the number at the position of the user input in the counts array, that way the array holds the number of times a number occurs in a specific index, e.g. counts holds how often 3 occurs.
counter array should contain frequency values for the numbers from 1 to 100 inclusive.
That is, either a shift by 1 should be used when counting the frequency:
int[] counter = new int[100];
for (int i = 0; i < n; i++) {
counter[scanner.nextInt() - 1]++;
}
or 101 may be used as the length of counter array thus representing values in the range [0..100], without shifting by 1.
int[] counter = new int[101];
for (int i = 0; i < n; i++) {
counter[scanner.nextInt()]++;
}
The minimal least frequent number can be found in a single loop (assuming that the counter length is 101).
int minFreq = 101, answer = -1;
for(int j = 1; j <= 100; j++) {
if (counter[j] > 0 && counter[j] < minFreq) { // check valid frequency > 0
minFreq = counter[j];
answer = j;
}
}
System.out.println(answer);
For a wider range of input values (e.g. including negative values) of a relatively small count it is better to use a hashmap instead of a large sparse array.
I am looking for a more elegant solution to the following scenario (language doesn't matter, Java is fine, but I am currently in C#). Suppose a linear array is coming in and being displayed as a table with X items per row (For instance, 9 items coming in at 3 items per row, so 3 rows of 3). If the array is being indexed 0 through 8, it is currently displayed as such:
6 7 8
3 4 5
0 1 2
So, the elements are displayed left to right, bottom to top. I would like to rearrange this to be displayed top to bottom, like this:
0 1 2
3 4 5
6 7 8
This requires reordering the array so that the new array's indexes are [6,7,8,3,4,5,0,1,2], with respect to the original's indexes. My current (untested) solution is the following: assume the array to be returned is 'array' and a temporary copy is 'temp', and the 'cols' variable is already the number of items per row.
int rows = (array.Length + cols - 1) / cols; //ceiling function to determine rows needed
int pos = array.Length - cols; //starting position in index transfer
int offset = 0; //needed when negative index reached
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
if (pos + j >= 0)
array[cols * i + j - offset] = temp[pos + j]; //assign values of temp to array
else
++offset; //takes care of negative indeces
}
pos -= cols;
}
return array;
The problem is this code is quite unreadable and possibly inefficient because of the double loop, though I don't expect many more than 9 items to come through. Is there a more elegant solution to this using slicing up the array, reversing, or anything that isn't so difficult to read? It's just a fun little problem I've been thinking about for an issue at work. Anybody's input is appreciated, thanks!
It is worth noting that potentially uneven tables can be created (For instance, 9 elements with 4 items per row creates 2 rows of 4, one row of 1. The 'offset' variable is used to protect negative array indexing if this is the case).
This is what I came up with: it doesn't sort your original array at all, but every inner loop starts at a computed starting-index based on the row you're printing. Let me know if you have any questions. This isn't specific to you, more specific to your situation, but the concept and functionality is there.
int[] array = new int[9] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
int rows = 3;
int cols = 3;
int count = 0;
for (int x = 1; x <= rows; x++)
{
int startingPos = array.Length - (x * rows);
for (int y = cols; y > 0; y--)
{
Console.Write(array[startingPos] +", ");
count++;
startingPos++;
}
Console.WriteLine();
}
I'm trying to create a method that will search through a 2d array of numbers. If the numbers add up to a certain sum, those numbers should remain and all of the other numbers should be changed to a 0. For example, if the desired sum is 7 and a row contains 2 5 1 2, the result should be 2 5 0 0 after the method is implemented. I have everything functioning but instead of keeping all of the numbers that add up to the sum, only the last number is retained. So, I am left with 0 5 0 0 . I think I need another array somewhere but not sure exactly how to go about implementing it. Any ideas?
public static int[][] horizontalSums(int[][] a, int sumToFind) {
int[][] b = new int[a.length][a[0].length];
int columnStart = 0;
while (columnStart < a[0].length) {
for (int row = 0; row < a.length; row++) {
int sum = 0;
for (int column = columnStart; column < a[row].length; column++) {
sum += a[row][column];
if (sum == sumToFind) {
b[row][column] = a[row][column];
}
}
}
columnStart++;
}
return b;
}
In your example you use 2 5 1 1, would 0 5 1 1 also be a valid response? Or do you just need to find any combination? A recursive function may be the best solution.
If you just need to scan through the array and add up the numbers until the sum is reached then just add a for loop to copy the previous values from the array to the new array when the sum is found. Something like:
if (sum == sumToFind)
{
for (int i= 0; i<= columnStart; i++)
{
b[row][i] = a[row][i];
}
}
if (sum == sumToFind)
{
for (int i= columnStart; i<= column; i++)
{
b[row][i] = a[row][i];
}
}
A minor tweak was all it needed. If you have columnStart and column like in the other answer, it only finds the first number of the series.
I need to randomly generate an array with 7 slots in Java. All these slots must have a value of at LEAST 1, but combined, have a total value of another defined number. They also all need to be an int value, no 1.5 or 0.9816465684646 numbers.
Example:
int a=10;
int[] ar = new int[7]
ar[0] = 1
ar[1] = 1
ar[2] = 2
ar[3] = 2
ar[4] = 1
ar[5] = 2
ar[6] = 1
I want it to generate something like that, but if int a=15, all the numbers would total 15 in any order
The standard way to generate N random numbers that add to a given sum is to think of your sum as a number line, generate N-1 random points on the line, sort them, then use the differences between the points as your final values. To get the minimum 1, start by subtracting N from your sum, run the algorithm given, then add 1 back to each segment.
public class Rand {
public static void main(String[] args) {
int count = 8;
int sum = 100;
java.util.Random g = new java.util.Random();
int vals[] = new int[count];
sum -= count;
for (int i = 0; i < count-1; ++i) {
vals[i] = g.nextInt(sum);
}
vals[count-1] = sum;
java.util.Arrays.sort(vals);
for (int i = count-1; i > 0; --i) {
vals[i] -= vals[i-1];
}
for (int i = 0; i < count; ++i) { ++vals[i]; }
for (int i = 0; i < count; ++i) {
System.out.printf("%4d", vals[i]);
}
System.out.printf("\n");
}
}
A good way to achieve uniformity is, for example, to fill up a = 15 units into an 8 element array:
Put 1 in each element in the array as this is your requirement, you have now 7 values left to distribute
Roll a random number between 0 and the max index of the array, and add 1 to that element, and subtract 1 from 7. Do this until 7 goes down to zero.
In this way, you'll meet your minimum conditions by having each element have minimum value 1. Then you distribute the remaining totals in a completely random way.
Adding on to what #Kon said, you could use two random numbers rather than one for more randomness. That is:
Fill every element in the array with the value 1
valuesToDistribute = a - array.length-1
randomIndex = Roll a number between 0 and array.length-1
randomValue = Roll a number between 1 and valuesToDistribute
Add to randomIndex the value randomValue
Subtract randomValue from valuesToDistribute
Repeat until valuesToDistribute = 0
My java is horrible, so I'm not providing the actual code here, as it would probably be wrong. I've done this exact thing in SQL before though, so I know it works...
Let Y be the Total value you want the elements to add up to
Begin a loop with variable Z going from 1 to X where X is the number elements in your array (here called AR)
In the loop, set AR(Z) to a random number between 1 and Y-X+Z
Subtract the new value from Y, so Y = Y - AR(Z)
End loop : back to step 2, advancing Z by 1