I have an array of numbers and another number K.
My task is to reduce the number of distinct elements in the array. For that, I can update the array several times. For updating the array, I have to follow these steps:
Select an element at index i and add that element by K, and reduce all other remaining elements by K.
For updating an array I can select the same index several times.
Example:
K = 1
Array: [3,1,3]
Answer: 3
I am picking index = 1, as [3-1, 1+1, 3-1] = [2,2,2] so we have number 2 that appears 3 times so this element occurs maximum number of times. So answer is 3.
Another example:
K = 1
Array: [1,2,2]
Answer: 2
It's not possible to make all elements same, so we have number 2 that appears 2 times, so answer is 2.
Array size can be [1, 1000], and the value of K and elements in array is in range [0, 1000]
Here is my code that I tried, my my approach is not correct.
public static int process(int K, int[] A) {
Map<Integer, Integer> map = new TreeMap<>();
for (int key : A) {
map.put(key, map.getOrDefault(key, 0) + 1);
}
int result = 0;
boolean flag = false;
int last = -1, cur = -1;
for (int key : map.keySet()) {
if (flag == false) {
flag = true;
last = key;
continue;
}
cur = key;
int a = map.get(last), b = map.get(cur);
if (Math.abs(last - cur) > K) {
result += a + b;
} else {
result += Math.max(a, b);
}
}
last = cur;
return result;
}
When looking at the examples with K = 1, it is clear that the answer
depends on the parity of the elements. Only elements with same parity can be set to the same level,
and all elements with same parity can be joined.
For example:
[2 4 6] -> [1 5 5] -> [2 4 4] -> [3 3 3]
[1 2 2] -> [2 1 1] ... no progress
With K = 1, we have to consider value modulo 2, i.e. modulo 2*K.
When K is different of one, for example K = 2, two numbers can be joined only there are separated by a distance multiple of 4, i.e. of 2*K.
[2 6 6] -> [4 4 4]
For K different from 1, instead of creating buckets for numbers with same parity,
we just create buckets according to value modulo 2K.
We just have to pay attention to use the modulo and not the remainder, the values are different for negative values.
Then the answer if simply the highest size of a bucket.
Output:
K = 1 Array : 3 1 3 -> 3
K = 1 Array : 1 2 2 -> 2
K = 1 Array : 2 3 4 7 4 9 11 -> 4
K = 1 Array : -3 -1 2 3 -> 3
K = 3 Array : -7 -1 0 1 2 4 5 -> 3
Here is a simple code in C++ to illustrate the algorithm.
In this code, the value val_modulo modulo 2K of each element is calculated.
Then, the orresponding counter is increased
Bucket[val_modulo] = Bucket[val_modulo] + 1
At the end, the highest value corresponds to the number of repetitions of the most repeated final value.
We may note that the number of non empty bucket corrresponds to the number of different
final values (not used in this code).
#include <iostream>
#include <vector>
#include <string>
#include <map>
void print (const std::vector<int> &A, const std::string &after = "\n", const std::string &before = "") {
std::cout << before;
for (int x: A) {
std::cout << x << " ";
}
std::cout << after;
}
int Modulo (int n, int mod) {
int ans = n % mod;
if (ans < 0) ans += mod;
return ans;
}
int max_equal(int K, std::vector<int> A) {
K = std::abs(K); // useful befoe taking the modulo
std::map<int, int> Buckets;
int nmax = 0;
int mod = 2*K;
for (int x: A) {
int val_modulo = Modulo (x, mod); // and not x*mod, as x can be negative
Buckets[val_modulo]++;
}
for (auto x: Buckets) {
if (x.second > nmax) {
nmax = x.second;
}
}
return nmax;
}
int main() {
std::vector<std::vector<int>> examples = {
{3, 1, 3},
{1, 2, 2},
{2, 3, 4, 7, 4, 9, 11},
{-3, -1, 2, 3},
{-7, -1, 0, 1, 2, 4, 5}
};
std::vector<int> tab_K = {1, 1, 1, 1, 3};
for (int i = 0; i < examples.size(); ++i) {
std::cout << "K = " << tab_K[i] << " Array : ";
print (examples[i], " -> ");
auto ans = max_equal (tab_K[i], examples[i]);
std::cout << ans << "\n";
}
return 0;
}
The problem is conceptual, and translating it in somewhat computing code.
Let's look:
We pick a number (by index, which is irrelevant), and for all the occurrences we add K. All others we subtract K And then the number of same occurrences must be maximal.
The same occurrences can only grow when the picked number + K is equal to another number - K.
The data structure:
A map with the array numbers as key, and mapped to frequency (how often the number occurs in the array).
So:
pickedNumber.value + K = otherNumber.value - K
=> otherNumber.value = pickedNumber.value + 2*K
Note that as there is only one single otherNumber, derived from the pickedNumber.
(It might occur more than once.)
And we want maximal:
pickedNumber.frequency + otherNumber.frequency maximal.
Though map is not really needed, a sorted array would do too, let's do a map:
The algorithm:
Kept simple.
int[] numbers = {3, 1, 3};
int index = pickedIndexOfBestSolution(numbers, 1);
System.out.println("Index: " + index);
int pickedIndexOfBestSolution(int[] numbers, int k) {
Map<Integer, Long> frequencyTable = IntStream.of(numbers)
.boxed()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
int bestNumber = frequencyTable.entrySet().stream()
.sorted(Comparator.comparingLong(e -> -e.getValue()
- frequencyTable.getOrDefault(e.getKey() + 2*k, 0L)))
.findFirst()
.map(e -> e.getKey())
.orElseThrow();
int index = -1;
while (numbers[++index] != bestNumber) {
}
return index;
}
The frequency table I filled using an IntStream and groupingBy (just as SQL).
As counting is done with long, I just kept that.
To find the max I counted the new occurrence count trying to add the "other" number's frequency too; 0 when no other number.
Instead of using .reverse() to reverse the comparison (largest, max, first), I took the negative value, which to me seems more calculatory.
Notice that a Stream with findFirst to find the max is probably optimal too: no need that the stream creates an intermediate list.
For the index I used brute force (while loop), a kind of indexOf.
Notice if there is no other number, it returns the index of a number with the most occurrences. Which is fine.
In short:
You see the different approach. Actually simpler, and more solid. In fact applying
some (minor) intelligence first. Trying to nail down the problem first.
Related
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 two arrays of ints, a and b, try to create an arithmetic sequence by adding ints from b into a. Return the maximum length of a or -1 if there does not exist an arithmetic sequence e.g. a = [2, 4, 8], b = [1, 6, 10, 12] -> a = [2, 4, 6, 8, 10, 12] -> return 6
I tried creating a new array and merging both a and b and counting the longest subsequence but the count could remove elements from a which should not be touched
static int maxSeq(int[] arr1, int[] arr2){
if(arr1.length ==0)return 0;
int n =arr1.length, m = arr2.length;
int[] arr = new int[n+m];
System.arraycopy(arr1,0,arr,0,n);
System.arraycopy(arr2,0,arr,n,m);
Arrays.sort(arr);
int result =0;
Map<Integer,Integer>[]d = new HashMap[n+m];
for(int i =0; i < arr.length;i++){
d[i] = new HashMap<Integer, Integer>();
}
for(int i =1; i < arr.length; ++i){
for(int j = 0; j<i;++j ){
int diff = arr[i]-arr[j];
int len =2;
if(d[j].containsKey(diff)){
len = d[j].get(diff) +1;
}
d[i].put(diff,len);
result = Math.max(result,d[i].get(diff));
}
}
return result;
}
a = [2, 4, 8], b = [1, 6, 10, 12] -> a = [2, 4, 6, 8, 10, 12] -> return 6
int[] a = {5,7,13,14}, b = {9,11,15}; return -1 not 6
I think you should try to fix your code.
if(d[j].containsKey(diff)){ len = d[j].get(diff) +1; }
Here you are looking for differences in a map of some index j, and there should be only one map of key value paires, not array of maps.
The key here is to fill in array A with numbers from array B so A become an arithmetic sequence.
Solution:
First find the minimum gap between 2 consequence number in A
With the given "gap", try to see if an arithmetic sequence can be build by looping through the 2 arrays, and find out if numbers in B can fill in array A so A become arithmetic with step = "gap". Take count of the length if success.
If it can be found, try to see if smaller gap can build longer arithmetic sequence by looping through all the divisor of the original "gap" as the new "gap" and test again. (example: A = [ 1,5], B= [3,7,9] => Previous step see that we can build arithmetic sequence [1,5,9], but the final answer should be [1,3,5,7,9].
I'd like to share my solution (probably is not optimized and I did not wrote many tests, but it worked on your two sample tests):
Idea:
Notice the common difference d of arithmetic sequence is upperbounded by min(a[i] - a[i-1]), otherwise we won't be able to visit all elements in a
We iterate on common difference d to check the length of each potential list, and find the max length
Complete code in Python:
(Suppose a, b are both sorted)
def max_arithmetic_length(a, b):
min_diff = float('inf') # common difference d is upper bounded by min_diff
for i in range(1, len(a)):
min_diff = min(min_diff, a[i] - a[i-1])
d_a = {x : True for x in a}
d_b = {x : True for x in b}
max_cnt = 0
for d in range(1, min_diff + 1):
skip_current_d = False # a switch to skip outer loop
for x in a:
if (x - a[0]) % d != 0: # must exist: a[i] - a[0] = kd
skip_current_d = True
break
if skip_current_d:
continue
cur = a[0]
cnt = 0
visited = {}
while cur in d_a or cur in d_b:
cnt += 1
visited[cur] = True
cur += d
if a[-1] not in visited: # if the last element in a is visited, then every element in a is visited
continue
# check those smaller than a[0] (may only exist in b)
cur = a[0] - d
while cur in b:
cnt += 1
cur -= d
max_cnt = max(cnt, max_cnt)
return max_cnt if max_cnt else -1
a = [2, 4, 8]
b = [1, 6, 10, 12]
print(max_arithmetic_length(a,b)) # return 6
a = [5,7,13,14]
b = [9,11,15]
print(max_arithmetic_length(a,b)) # return -1
I am trying to solve:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
Here is my solution:
def twoSum(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
hash_table = {}
k = target
for i, x in enumerate(nums):
if x not in hash_table:
hash_table[x] = i
for x in nums:
if k-x in hash_table:
if hash_table[k-x]!= hash_table[x]:
return [hash_table[x], hash_table[k-x]]
Now the solution is not correct as it fails the test case like [3,3], 6. Now both 3s get stored in hash table as one entry which is expected, so only one index is recorded in the hash table for 3 and my solution doesn't work.
So, I think the solution might not be with hash tables. But the correct solution is:
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
Now, this is essentially the same solution in Java and it is mentioned as the correct one.
So, my question is:
How can I change my solution in Python to work and not fail the test case?
How is the Java solution different, does its hash table have some other behavior?
Thanks for your help.
The Java solution has a check that takes care of two equal elements:
if (map.containsKey(complement) && map.get(complement) != i)
The first part of this condition - map.containsKey(complement) - means that the number complement is present in the Map, while the second part - map.get(complement) != i) - means that the index of complement stored in the map is different from the index i. This means that if complement == nums[i], there are two identical numbers in the input array.
I don't know Python, but it looks like your code fails because
if hash_table[k-x]!= hash_table[x]
always returns false when k-x == x. You need to compare hash_table[k-x] to the current index of the input array.
Based on your first Python loop, I'm assuming the second loop should look like this:
for i, x in enumerate(nums):
if k-x in hash_table:
if hash_table[k-x]!= i:
return [i, hash_table[k-x]]
Why not go with something simple as:
def twoSum(nums, target):
for i, num1 in enumerate(nums):
for j, num2 in enumerate(nums):
if num1 + num2 == target and i != j:
return [i, j]
return [-1, -1] # or whaterver you want to say "no solution found"
this produces:
print twoSum([2, 7, 11, 15], 9) # =>[0, 1] 2+7
print twoSum([2, 7, 11, 15], 22) # =>[1, 3] 7+15
print twoSum([2, 7, 11, 15], 23) # => [-1, -1] no solution
Alternatively to the hash table solution, you can
sort the array (time O(N Log N)),
maintain two indexes i, j such that num[i] + num[j] <= sum < num[i] + num[j+1]; every time you increase i, you decrease j by 0 or more to adjust. i starts from 0 and j from the end.
At worse you increase i N times and decrease j N times for a total of O(N) operations. In case of equality, you are done.
This can be done in-place.
Tried out using the JavaScript,
let qarr = [2, 3, 5, 6, 7, 9, 21, 24, 27, 35, 50, 98] //sorted Array
const qk = 85
function check_sum(arr, k){
for( i in arr){
if(arr[i]+arr[arr.length-1] === k){
return [qarr.indexOf(arr[i]), qarr.indexOf(arr[arr.length-1])]
}
else{
if(arr[i]+arr[arr.length-1] >= k){
if(arr.length == 2){
return arr[0]+arr[1] == k ? [qarr.indexOf(arr[0], qarr.indexOf(arr[1]))] : [null, null]
}
else if(arr.length == 1){
return arr[0]*2 == k ? [qarr.indexOf(arr[0]), qarr.indexOf(arr[0])] : [null, null]
}
else{
return check_sum(arr.slice(i), k)
}
}
}
}
}
res = check_sum(qarr.filter(a=> a<=qk), qk)
console.log('result', res)
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
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
Right now I'm trying to write a function that takes an array and an integer n, and gives a list of each size n combination (so a list of int arrays). I am able to write it using n nested loops, but this only works for a specific size of subset. I can't figure out how to generalize it to work for any size of combination. I think I need to use recursion?
This is the code for all combinations of 3 elements, and I need an algorithm for any number of elements.
import java.util.List;
import java.util.ArrayList;
public class combinatorics{
public static void main(String[] args) {
List<int[]> list = new ArrayList<int[]>();
int[] arr = {1,2,3,4,5};
combinations3(arr,list);
listToString(list);
}
static void combinations3(int[] arr, List<int[]> list){
for(int i = 0; i<arr.length-2; i++)
for(int j = i+1; j<arr.length-1; j++)
for(int k = j+1; k<arr.length; k++)
list.add(new int[]{arr[i],arr[j],arr[k]});
}
private static void listToString(List<int[]> list){
for(int i = 0; i<list.size(); i++){ //iterate through list
for(int j : list.get(i)){ //iterate through array
System.out.printf("%d ",j);
}
System.out.print("\n");
}
}
}
This is a well-studied problem of generating all k-subsets, or k-combinations, which can be easily done without recursion.
The idea is to have array of size k keeping sequence of indices of elements from the input array (which are numbers from 0 to n - 1) in increasing order. (Subset then can be created by taking items by these indices from the initial array.) So we need to generate all such index sequences.
First index sequence will be [0, 1, 2, ... , k - 1], on the second step it switches to [0, 1, 2,..., k], then to [0, 1, 2, ... k + 1] and so on. The last possible sequence will be [n - k, n - k + 1, ..., n - 1].
On each step, algorithm looks for the closest to the end item which can be incremented, increments it and fills up items right to that item.
To illustrate, consider n = 7 and k = 3. First index sequence is [0, 1, 2], then [0, 1, 3] and so on... At some point we have [0, 5, 6]:
[0, 5, 6] <-- scan from the end: "6" cannot be incremented, "5" also, but "0" can be
[1, ?, ?] <-- "0" -> "1"
[1, 2, 3] <-- fill up remaining elements
next iteration:
[1, 2, 3] <-- "3" can be incremented
[1, 2, 4] <-- "3" -> "4"
Thus, [0, 5, 6] is followed by [1, 2, 3], then goes [1, 2, 4] etc.
Code:
int[] input = {10, 20, 30, 40, 50}; // input array
int k = 3; // sequence length
List<int[]> subsets = new ArrayList<>();
int[] s = new int[k]; // here we'll keep indices
// pointing to elements in input array
if (k <= input.length) {
// first index sequence: 0, 1, 2, ...
for (int i = 0; (s[i] = i) < k - 1; i++);
subsets.add(getSubset(input, s));
for(;;) {
int i;
// find position of item that can be incremented
for (i = k - 1; i >= 0 && s[i] == input.length - k + i; i--);
if (i < 0) {
break;
}
s[i]++; // increment this item
for (++i; i < k; i++) { // fill up remaining items
s[i] = s[i - 1] + 1;
}
subsets.add(getSubset(input, s));
}
}
// generate actual subset by index sequence
int[] getSubset(int[] input, int[] subset) {
int[] result = new int[subset.length];
for (int i = 0; i < subset.length; i++)
result[i] = input[subset[i]];
return result;
}
If I understood your problem correctly, this article seems to point to what you're trying to do.
To quote from the article:
Method 1 (Fix Elements and Recur)
We create a temporary array ‘data[]’ which stores all outputs one by
one. The idea is to start from first index (index = 0) in data[], one
by one fix elements at this index and recur for remaining indexes. Let
the input array be {1, 2, 3, 4, 5} and r be 3. We first fix 1 at index
0 in data[], then recur for remaining indexes, then we fix 2 at index
0 and recur. Finally, we fix 3 and recur for remaining indexes. When
number of elements in data[] becomes equal to r (size of a
combination), we print data[].
Method 2 (Include and Exclude every element)
Like the above method, We create a temporary array data[]. The idea
here is similar to Subset Sum Problem. We one by one consider every
element of input array, and recur for two cases:
The element is included in current combination (We put the element in data[] and increment next available index in data[])
The element is excluded in current combination (We do not put the element and do not change index)
When number of elements in data[] become equal to r (size of a
combination), we print it.