Related
I was trying one problem to count the number of triplets in an array whose sum is less than target value.
Input: [-1, 4, 2, 1, 3], target=5
Output: 4
Explanation: There are four triplets whose sum is less than the target:
[-1, 1, 4], [-1, 1, 3], [-1, 1, 2], [-1, 2, 3]
My Code
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target - arr[i] - arr[left] - arr[right];
if (targetDiff > 0)
{
count++;
right--;
}
else
{
left++;
}
}
}
// TODO: Write your code here
return count;
}
}
It produces the output of 3 where as correct value should be 4 as per the above given input. My logic was , say , x + y + z < targetSum , it implies (targetSum - (x + y + z) ) > 0. If this is true I will increase the count and then decrement the right pointer , since array is sorted. If its not true then I will increment the left pointer . But my logic does not cover the triplet {-1, 2, 3}.
Below is the correct code given by author.
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for (int i = 0; i < arr.length - 2; i++) {
count += searchPair(arr, target - arr[i], i);
}
return count;
}
private static int searchPair(int[] arr, int targetSum, int first) {
int count = 0;
int left = first + 1, right = arr.length - 1;
while (left < right) {
if (arr[left] + arr[right] < targetSum) {
count += right - left;
left++;
} else {
right--; // we need a pair with a smaller sum
}
}
return count;
}
public static void main(String[] args) {
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 0, 2, 3 }, 3));
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 4, 2, 1, 3 }, 5));
}
}
The author has used the concept , say x + y + z < targetSum , it implies x + y < targetSum - z . But I don't get the logic of line count += right - left; . How author use this one line to capture the count. If some one can give me the intution on how to reach this inference. Also what is wrong with my code and what can I do to correct it.
A first issue with your code is that :
you only decrease the right index if the sum is inferior to the target.
However, since you have ordered your list, well you will only be entering that case until left=right.
Quick example : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], target=14
if 1+2+10 <13:
then you will only decrease 10 until you reach 2 in your array
and then you proceed to iterate to the next i-index, here going from 0 to 1.
Meaning that: you will never get the solutions in between such as [1,3,9] and all the one that follows.
I hope it helps you see where there was an error in the logic, which was not from the statement : (targetSum - (x + y + z) ) > 0 but from the action you take according to the result (True/False).
Now, I am not sure there would be an easy way to adapt your code corrctly, because the main issue here is that you have iterate over 2 indexes at once (right and left).
Now regarding your author's answer :
The trick behind :
count += right - left;
goes back to the issue you had, if i tame my example, for
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
it is basically saying that, since the array is ordered, if the sum of two integers with the right one is inferior to target, then it will also be true for all integers inferior to right :
1+2+10<14 => 1+2+9<13
And this statement is true for all integers between left and right, so instead of doing a loop for which we already have the answer, he adds to count the differences between right and left, in other words, the number of integers in your array that will be greater than left and lower than right.
Now that i have explained that, you could use the same "trick" to your code:
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target -( arr[i] + arr[left] + arr[right]);
if (targetDiff > 0)
{
count += right - left;
left++;
}
else
{
right--;
}
}
}
// TODO: Write your code here
return count;
}
}
I tried to be as detailed as possible, hope it helps you understand better!
I'm working on the following task.
Given an array of n integers and two integer numbers m and k.
You can add any positive integer to any element of the array such that
the total value does not exceed k.
The task is to maximize the
multiples of m in the resultant array.
Consider the following example.
Input:
n = 5, m = 2, k = 2, arr[] = [1, 2, 3, 4, 5]
Let's add 1 to the element arr[0] and 1 to arr[2] then the final array would be:
[2, 2, 4, 4, 5]
Now there are four (4) elements which are multiples of m (2).
I am not getting correct output.
My code:
public class Main {
public static void main(String[] args) {
int n = 5;
int m = 4;
int k = 3;
int count = 0;
int[] arr = {17, 8, 9, 1, 4};
for (int i = 0; i < n; i++) {
for (int j = 0; j <= k; j++) {
// check initial
if (arr[i] % m == 0) {
break;
}
// add
arr[i] = arr[i] + j;
// check again
if (arr[i] % m == 0) {
count++;
break;
}
}
}
System.out.println("Final Array : " + Arrays.toString(arr));
System.out.println("Count : " + count);
}
}
This task boils down to a well-known Dynamic programming algorithm called Knapsack problem after a couple of simple manipulations with the given array.
This approach doesn't require sorting and would be advantages when k is much smaller n.
We can address the problem in the following steps:
Iterate over the given array and count all the numbers that are already divisible by m (this number is stored in the variable count in the code below).
While iterating, for every element of the array calculate the difference between m and remainder from the division of this element by m. Which would be equal to m - currentElement % m. If the difference is smaller or equal to k (it can cave this difference) it should be added to the list (differences in the code below) and also accumulated in a variable which is meant to store the total difference (totalDiff). All the elements which produce difference that exceeds k would be omitted.
If the total difference is less than or equal to k - we are done, the return value would be equal to the number of elements divisible by m plus the size of the list of differences.
Otherwise, we need to apply the logic of the Knapsack problem to the list of differences.
The idea behind the method getBestCount() (which is an implementation Knapsack problem) boils down to generating the "2D" array (a nested array of length equal to the size of the list of differences +1, in which every inner array having the length of k+1) and populating it with maximum values that could be achieved for various states of the Knapsack.
Each element of this array would represent the maximum total number of elements which can be adjusted to make them divisible by m for the various sizes of the Knapsack, i.e. number of items available from the list of differences, and different number of k (in the range from 0 to k inclusive).
The best way to understand how the algorithm works is to draw a table on a piece of paper and fill it with numbers manually (follow the comments in the code, some intermediate variables were introduced only for the purpose of making it easier to grasp, and also see the Wiki article linked above).
For instance, if the given array is [1, 8, 3, 9, 5], k=3 and m=3. We can see 2 elements divisible by m - 3 and 9. Numbers 1, 8, 5 would give the following list of differences [2, 1, 1]. Applying the logic of the Knapsack algorithm, we should get the following table:
[0, 0, 0, 0]
[0, 0, 1, 1]
[0, 1, 1, 2]
[0, 1, 2, 2]
We are interested in the value right most column of the last row, which is 2 plus 2 (number of elements divisible by 3) would give us 4.
Note: that code provided below can dial only with positive numbers. I don't want to shift the focus from the algorithm to such minor details. If OP or reader of the post are interested in making the code capable to work with negative number as well, I'm living the task of adjusting the code for them as an exercise. Hint: only a small change in the countMultiplesOfM() required for that purpose.
That how it might be implemented:
public static int countMultiplesOfM(int[] arr, int k, int m) {
List<Integer> differences = new ArrayList<>();
int count = 0;
long totalDiff = 0; // counter for the early kill - case when `k >= totalDiff`
for (int next : arr) {
if (next % m == 0)
count++; // number is already divisible by `m` we can increment the count and from that moment we are no longer interested in it
else if (m - next % m <= k) {
differences.add(m - next % m);
totalDiff += m - next % m;
}
}
if (totalDiff <= k) { // early kill - `k` is large enough to adjust all numbers in the `differences` list
return count + differences.size();
}
return count + getBestCount(differences, k); // fire the rest logic
}
// Knapsack Algorithm implementation
public static int getBestCount(List<Integer> differences, int knapsackSize) {
int[][] tab = new int[differences.size() + 1][knapsackSize + 1];
for (int numItemAvailable = 1; numItemAvailable < tab.length; numItemAvailable++) {
int next = differences.get(numItemAvailable - 1); // next available item which we're trying to place to knapsack to Maximize the current total
for (int size = 1; size < tab[numItemAvailable].length; size++) {
int prevColMax = tab[numItemAvailable][size - 1]; // maximum result for the current size - 1 in the current row of the table
int prevRowMax = tab[numItemAvailable - 1][size]; // previous maximum result for the current knapsack's size
if (next <= size) { // if it's possible to fit the next item in the knapsack
int prevRowMaxWithRoomForNewItem = tab[numItemAvailable - 1][size - next] + 1; // maximum result from the previous row for the size = `current size - next` (i.e. the closest knapsack size which guarantees that there would be a space for the new item)
tab[numItemAvailable][size] = Math.max(prevColMax, prevRowMaxWithRoomForNewItem);
} else {
tab[numItemAvailable][size] = Math.max(prevRowMax, prevColMax); // either a value in the previous row or a value in the previous column of the current row
}
}
}
return tab[differences.size()][knapsackSize];
}
main()
public static void main(String[] args) {
System.out.println(countMultiplesOfM(new int[]{17, 8, 9, 1, 4}, 3, 4));
System.out.println(countMultiplesOfM(new int[]{1, 2, 3, 4, 5}, 2, 2));
System.out.println(countMultiplesOfM(new int[]{1, 8, 3, 9, 5}, 3, 3));
}
Output:
3 // input array [17, 8, 9, 1, 4], m = 4, k = 3
4 // input array [1, 2, 3, 4, 5], m = 2, k = 2
4 // input array [1, 8, 3, 9, 5], m = 3, k = 3
A link to Online Demo
You must change 2 line in your code :
if(arr[i]%m==0)
{
count++; // add this line
break;
}
// add
arr[i]=arr[i]+1; // change j to 1
// check again
if(arr[i]%m==0)
{
count++;
break;
}
The first is because the number itself is divisible.
and The second is because you add a number to it each time.That is wrong.
for example chenge your arr to :
int[] arr ={17,8,10,2,4};
your output is :
Final Array : [20, 8, 16, 8, 4]
and That is wrong because 16-10=6 and is bigger than k=3.
I believe the problem is that you aren't processing the values in ascending order of the amount by which to adjust.
To solve this I started by using a stream to preprocess the array. This could be done using other methods.
map the values to the amount to make each one, when added, divisible by m.
filter out those that equal to m' (already divisible by m`)
sort in ascending order.
Once that is done. Intialize max to the difference between the original array length and the processed length. This is the number already divisible by m.
As the list is iterated
check to see if k > amount needed. If so, subtract from k and increment max
otherwise break out of the loop (because of the sort, no value remaining can be less than k)
public static int maxDivisors(int m, int k, int[] arr) {
int[] need = Arrays.stream(arr).map(v -> m - v % m)
.filter(v -> v != m).sorted().toArray();
int max = arr.length - need.length;
for (int val : need) {
if (k >= val) {
k -= val;
max++;
} else {
break;
}
}
return max;
}
int m = 4;
int k = 3;
int[] arr ={17,8,9,1,4};
int count = maxDivisors(m, k, arr);
System.out.println(count);
prints
3
Given an array of integers, I'm trying to find the longest subset (powerset) with sum equal to k using the lease possible time complexity.
e.g. if inputArr= [1, 2, 8, 1, 1, 7] and k= 10, then the output should be 4 since the longest subset with sum equals to 10 is [1, 1, 1, 7].
Edit: I might've forgotten an important detail; the elements of the array are all positive and non-zero.
I used this algorithm that I found on geeksforgeeks:
https://www.geeksforgeeks.org/finding-all-subsets-of-a-given-set-in-java/
The code works fine, but the only problem that I have is with the execution time. I am supposed to submit this online, and when I submit it the execution terminates due to timeout.
int maxSubLength=0;
for (int i = 1; i < (1<<n); i++) //n is the length of inputArr
{
int sum=0, length=0;
for (int j = 0; j < n; j++)
if ((i & (1 << j)) > 0)
{
sum+=inputArr[j];
length++;
if (sum>k)
break;
}
if (sum==k)
maxSubLength=Math.max(maxSubLength, length);
}
Is there any faster algorithm? I tried a recursive one and it didn't help.
We can solve this with dynamic programming in O(n*k) time and O(k) space. JavaScript code:
function f(A, K){
let m = new Array(K + 1).fill(0)
for (let a of A){
for (let k=K; k>=a; k--)
if (m[k - a])
m[k] = Math.max(m[k], 1 + m[k - a])
m[a] = Math.max(m[a], 1)
}
return m[K]
}
var A = [1, 2, 8, 1, 1, 7]
var K = 10
console.log(f(A, K))
I have two arrays of different sizes that I am trying to loop through and perform calculations on, but I am ending up with incorrect outputs.
Here are the arrays:
int[] array1 = [5, 10, 2]
int[] array2 = [11, 23, 4, 6, 5, 8, 9]
int[] array1 = {5, 10, 2};
int[] array2 = {11, 23, 4, 6, 5, 8, 9};
ArrayList<Integer> calculationsArray = new ArrayList<Integer>();
int calculations = 0;
for(int i = 0; i < array1.length; i++) {
for(int j = 0; j < array2.length; j++) {
calculations = ((array1[i] + array2[j]) % 10);
}
calculationsArray.add(calculations);
}
I expect the output of [6, 3, 6, 1, 0, 8, 1]. Example calculations for 4 loops would be:
(11 + 5) % 10 = 6
(23 + 10) % 10 = 3
(4 + 2) % 10 = 6
(6 + 5) % 10 = 1
But the actual output is: [4, 9, 1]
Let's first talk about why you're seeing what you're seeing.
I've reposted your code here, with the indentation changed to reflect the nesting:
for(int i = 0; i < array1.length; i++) {
for(int j = 0; j < array2.length; j++) { // <--- This loop
calculations = ((array1[i] + array2[j]) % 10);
}
calculationsArray.add(calculations);
}
Let's take the inner loop, the one that I've marked above. Notice that on each iteration of the loop, you set calculations to a new value. This overwrites the value that was computed earlier, so that when the inner loop finishes running, the value of calculations will be equal to the last value that's computed in that loop. And that accounts for what you're actually seeing, because
when i = 0, the inner loop's last calculation is (array1[0] + array2[6]) % 10, which is (5 + 9) % 10 = 4,
when i = 1, the inner loop's last calculation is (array1[1] + array2[6]) % 10, which is (10 + 9) % 10 = 9,
when i = 2, the inner loop's last calculation is (array1[2] + array2[6]) % 10, which is (2 + 9) % 10 = 1.
And hey, look! There's your [4, 9, 1] that you're seeing.
The next question is how to go about fixing this. The major issue I believe that you're running into here is the fact that doubly-nested loops probably isn't the way to go here. Specifically, a double for loop here will run array1.length × array2.length times, which is way more than the number of times that you need it to run.
So a first question - how many times should the loop run? Well, you want your output array to have the length of the longer of the two input arrays, which is Math.max(array1.length, array2.length) times. Could you make a single loop that counts up to that number?
You then need to handle the fact that your loop may need to cycle through each input array multiple times. In the example you've given, you'll read the values of array1 multiple times because array2 is longer, but you just as easily could have had to read the values of array2 multiple times if array1 were longer.
As a hint for how to do this, see if you can use the mod operator % to wrap around when the indices get too big.
Hope this helps!
What about somenthing like (not optimized):
ArrayList<Integer> calculationsArray = new ArrayList<Integer>();
int calculations = 0;
int j = 0;
while (j < array2.size()) {
for (int i = 0; i < array1.size(); i++) {
calculations = ((array1.get(i) + array2.get(j)) % 10);
calculationsArray.add(calculations);
}
j++;
}
Given an int array of length n, split up the array into 3 parts and make sure that the 2 smaller parts are as large as possible.
Splitting rules:
pick two indices a and b of the given array (0 <= a <= b <= n-1)
Size of first part is: sum of all array entries from index 0 to a-1 (inclusive)
Size of second part is: sum of all array entries from index a to b (inclusive)
Size of third part is: sum of all array entries from b+1 to n-1 (inclusive)
empty parts are possible..
The expected output is the sum of the 2 smaller parts (their sizes).
Example, an array ofn = 6 and some values are given.
The solution calculates a = 2, b = 3 which splits the array into 3 parts: left part is of size 6 + 7 = 13, middle part is 8 + 9 = 17, right part is 4 + 5 = 9. Output is 13 + 9 = 22 (sum of the 2 smaller parts).
Graphical representation:
More examples:
[6, 8, 3, 5, 7, 2, 4, 6] should be split up into:
Left (6 + 8 = 14)
Middle (3 + 5 + 7 = 15)
Right (2 + 4 + 6 = 12)
Output is 14 + 12 = 26 (sum of the 2 smaller parts)
[9, 12, 4, 7, 10, 2, 5, 8, 11, 3] should be split up into:
Left (9 + 12 + 4 = 25)
Middle (7 + 10 + 2 + 5 = 24)
Right (8 + 11 + 3 = 22)
Output is 22 + 24 = 46 (sum of the 2 smaller parts)
My approach doesn't work for the given test cases:
// L is size of left part, M is size of middle part, R is size of right part
/* I start with all array entries in the middle part, then I put elements
out of the middle part into the left and right part (depending on which
is smaller) until one of them is larger than M, this approach works for
many cases, two exceptions are the first 2 arrays given as examples in
this post.
*/
long a = 1;
long b = n;
long L = R = 0;
long M = arr.sumOfAllArrayEntries;
long temp;
long[] arr = {9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
while (M > Math.max(L, R)) {
if (L < R) {
// move leftmost element of M to L
temp = arr[(int) a++];
M -= temp;
L += temp;
}
else {
// move rightmost element of M to R
temp = arr[(int) b--];
M -= temp;
R += temp;
}
}
// finds maximum of M, L, R
temp = Math.max(M, Math.max(L, R));
// finds 2 smallest numbers out of M, L, R
if (temp == M)
temp = L + R;
else if (temp == L)
temp = M + R;
else if (temp == R)
temp = M + L;
// temp is equal to the sum of the 2 smaller parts
System.out.println("Output: " + temp);
The basic idea that comes to mind:
Loop over all positions for a.
Loop over all positions for b.
Calculate the left, mid and right sum.
Calculate the target sum and store this if it's better than the best sum we've seen.
This can be optimised to O(n) by noting a few things:
For any given position b, the best position for a will always be, sum-wise, in the middle of b and the start (specifically at the point that minimises the difference between the left and mid sums).
The best position for a can't move left as b moves right (since that will decrease left sum for a bigger mid sum, decreasing the target sum).
This means we only need one loop over b, while keeping track of a as we go, increasing it when appropriate.
We can keep track of the sums as we go.
This gives us the following code:
int arr[] = {9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
int sum = 0;
for (int i: arr)
sum += i;
int a = 0;
int left = 0, mid = 0;
int best = 0;
for (int b = 0; b < arr.length; b++)
{
mid += arr[b];
// since this loop increases `a` with every iteration, and `a` never resets,
// it will not run more than O(n) times in total
while (a < b && Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid))
{
left += arr[a];
mid -= arr[a];
a++;
}
int right = sum - mid - left;
best = Math.max(best,
mid + left + right - Math.max(mid, Math.max(left, right)));
}
System.out.println(best);
Live demo.
The problem with your approach is when you get into a situation like:
a b
6 7 | 8 9 | 4 5
L=13 M=17 R=9
M > Math.max(L, R) will be true, so you'll move one of the elements, despite already having the best split.
Note how I did Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid) in my code instead of simply left < mid. You will need something similar to check whether you should continue.
An example you'd need to consider is one where you need to further increase the bigger side:
100 | 10 120 | 90 -> 100 10 | 120 | 90
That might complicate your code quite a bit more.
This can be done using concept of Two Pointers.So first see main array as concatenation of 3 sub-arrays. A,B and C. Now we can first calculate total sum of all elements of array which indicates the considered array has all the elements.
So now we need to keep track of summation of 3 continuous subarrays of the original array. Consider here that we have 3 arrays here as
A ---> Starting from the left-side (index 0)
B ---> Middle sub-array
C ---> Starting from the right-side (index n-1)
Here answer should be min(sumOfA,min(sumOfB,sumOfC)) which is to be maximized.
Here we have stored summation of all elements in sub-array B considering it has all elements of array. A and C are empty. Now we will remove one by one element from either of the end and add that value to appropriate sub-array A or C and we need to delete it from the B by subtracting it.
Now the question remains that which element is to be removed. For this we will check value of A and C and whoever has lower sum than other one, we will add elements from that end to the specific sub-array.
Another problem here may arise is Termination Condition. Here termination condition would be Sum of B > Sum of A && Sum of B > Sum of C. So when sum of B becomes lesser than any of the other two sub arrays, we need to stop there.
Complexity of this approach : O(n)
Code :
import java.util.*;
class Main
{
public static void main(String args[])
{
long arr[]={9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
long sumOfA=0;
long sumOfB=0;
long sumOfC=0;
int a = 0; //set end of sub-array A
int b = arr.length-1; //set start of sub-array-C
long maximum =0; // Minimum of sum of all subarrays should be maximum,
// That will be sufficient to get the answer
long answer=0;
int answer_a=0;
int answer_b=0;
for(int i=0;i<arr.length;i++)
{
sumOfB+=arr[i];
}
for(int i=0;i<arr.length;i++)
{
long minimum = Math.min(sumOfA , Math.min(sumOfB,sumOfC));
if(minimum>=maximum)
{
answer_a=a;
answer_b=b;
ArrayList<Long> list=new ArrayList<Long>(); //To calculate the answer
list.add(sumOfA);
list.add(sumOfB);
list.add(sumOfC);
Collections.sort(list);
answer=Math.max(answer,list.get(0)+list.get(1)); //take minimum two elements
maximum=minimum;
}
if(sumOfB < sumOfC || sumOfB < sumOfA)
break;
if(a>=b) //If both pointer passes to each other
break;
if(sumOfA == sumOfC)
{
if(arr[a]<arr[b]) //take minimum element
{
sumOfA+=arr[a];
sumOfB-=arr[a];
a++; //move a to next element
}
else
{
sumOfC+=arr[b];
sumOfB-=arr[b];
b--; //move b to prev element
}
}
else if(sumOfA > sumOfC)
{
sumOfC+=arr[b];
sumOfB-=arr[b];
b--;
}
else
{
sumOfA+=arr[a];
sumOfB-=arr[a];
a++;
}
}
System.out.println("a(exclsive) : "+answer_a);
System.out.println("b(exclsive) : "+answer_b);
System.out.println("Answer : "+answer);
}
}
Answer for [9, 12, 4, 7, 10, 2, 5, 8, 11, 3] :
a(exclsive) : 3
b(exclsive) : 6
Answer : 46
Answer for [6, 8, 3, 5, 7, 2, 4, 6] :
a(exclsive) : 2
b(exclsive) : 4
Answer : 26