Cover a given range of an array - java

I'm trying to figure out the algorithm to implement a method and I would need some advice in the regard. I have given an array and range,
A = [1, 15, 30, 40, 50]
range = 10
I need to figure out the minimum number of points that would cover all the numbers of the given array.
Cover means that distance you can reach both left and right with the provided range. For example, if I have a point at 23 and range of 5, then I can reach 18-23 left side and 23-28 right side. The points can cover both the left and the right sides of the given range.
In the provided array the result is 3. We would need one point at 1, another at 15, and 3rd one at 40. I explain it below,
i. The point at 1 will cover till 10 (1-10)
ii. The point at 15 will cover itself (15)
iii. The point at 40 will cover left till the 30 and at the right till the 50 (30-40-50)
Please, provide a good reason before you attempt to downvote. Thank you.

If the array is sorted one can use greedy algorithm. I also assume that all number in the input are positive.
Consider the following steps:
1. Int cover = -1, points= new array
2. For each element in the array:
2.1 . If element > cover then:
2.1.1 cover = element + range - 1 // biggest value that still cover the element
2.1.2 push to point cover value
The points array will contain the mimium points needed to cover the array for given range
Complexity: assuming n is the size of the input array:
Time complexity is O(n) if the array is sorted and O(nlogn) if not. Space complexity is O(k) when k is the number of cover points which bounded by n. If all you want is the number of the points then space complexity is O(1)
If the points can only be chosen from the array you can modify the algorithm as follow:
1. Int cover = -1, points= new array
2. For each i in the array.length:
2.1 If array[i] > cover then:
2.1.1 cover = array[i] //element can cover itself
2.1.2 for (j=i+1; array[j] < array[i] + range; j++) // find the largest element who still cover array[i]
2.1.2.1 cover = array[j] // element in index j cover also index i
2.1.3 push to point cover value
2.1.4 add range to cover // to mark for cover after a[j]
The space complexity is the same. Time complexity is also O(n) - because every step the inner loop (j) is doing the i-loop we fail the condition of the if statement - so we stay with O(n).

I'll try to make it concise by using the Stream API if I get time:-
public static void main(String[] args) {
int[] arr = {1, 15, 30, 40, 50};
int range = 10;
Map<Integer, Set<Integer>> map = new HashMap<>(arr.length); //maps indexes of elements to the indexes of elements it covers
for (int i = 0; i < arr.length; i++) {
for (int j : arr) {
if (j >= arr[i] - range && j <= arr[i] + range) {
if (map.get(i) == null) {
Set<Integer> set = new HashSet<>();
set.add(j);
map.put(i, set);
} else {
map.get(i).add(j);
}
}
}
}
Set<Integer> mapEntriesToDelete = new HashSet<>();
for (Map.Entry<Integer, Set<Integer>> e : map.entrySet()) {
for (Map.Entry<Integer, Set<Integer>> f : map.entrySet()) {
if (!e.getKey().equals(f.getKey()) && e.getValue().containsAll(f.getValue())) {
mapEntriesToDelete.add(f.getKey());
}
}
}
mapEntriesToDelete.forEach(map::remove);
System.out.println("Points: " + map.size());
System.out.print("At indexes: ");
map.keySet().forEach(k -> System.out.print(k + ","));
}
Output
Points: 3
At indexes: 0,1,3,

I programmed the algorithm proposed by David in the earlier answer. The code is provided below,
public static int getMinNumberOfPoints(int R, int[] A) {
int cover = -1;
int N = A.length;
List<Integer> list = new ArrayList<>();
int count = 0;
for (int i = 0; i < N; i++) {
if (A[i] > cover) {
cover = A[i];
/*
* find the largest element who still cover A[i]
* */
for (int j = i + 1; j < N && A[i] + R >= A[j]; j++) {
cover = A[j];
}
/*
* add the cover to keep track for camera placement
* */
list.add(cover);
count++;
cover += R;
}
}
return count;
}

Related

Find all combinations of an array and get top k sum elements

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;
}

Total all matching integer elements in an array

I was looking over some basic questions that might be asked in an interview. Using basic for loops (No hash maps etc)I want to sum up all the matching elements in an array.For example, 6 matching elements will result in (Matched: 6) and a total of 36 in the example below.An example of this could be rolling dice, and the score is the total of all the dice that match.
public static void main(String[] args) {
int arr[] = {6,6,6,6,6,6};
int matched = 1;
int value = 0;
int total = 0;
for(int i=0;i<arr.length;i++){
for(int j=(i+1);j<arr.length;j++){
if(ar[i] == ar[j]){
matched++;
value = ar[i];
break;
}
}
total = (matched * value);
} // End for loop
System.out.println("Matched:"+(matched)+"");
System.out.println("Total:"+total);
}
But, if the array was for example...
int arr[] = {6,1,1,6,6,6};
The output I get will be (Matched:5) and an a total of 30.Can could I store the matching pair of 1's and add them to the total using as little basic code as possible?
Interpreting the question as "provide the total number of values that occur more than once and their sum", and taking into account that nothing "fancy" (sic) such as a map can be used:
int[] array = {6, 1, 1, 6, 6, 6};
int sum = 0;
int matches = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if (i != j && array[i] == array[j]) {
sum += array[i];
matches++;
break;
}
}
}
System.out.println(matches); // 6
System.out.println(sum); // 26
If you are allowed to use arrays - if not too fancy - then you could use 3 loops:
The first finds minimum and maximum elements
The second determines the occurrence of each item
The third calculates the totals
In Java this would look like this:
public static void main(String[] args) {
int[] array = {6, 3, 3, 6, 6, 6, 4, 4, 20};
int min = array[0];
int max = array[0];
// Find min max
for (int i : array) {
if (i < min) {
min = i;
}
if (i > max) {
max = i;
}
}
// Use array to sum up all elements
int[] holderArray = new int[max - min + 1];
for (int i : array) {
holderArray[i - min]++;
}
// Calculate occurrence and sums
for(int i = 0; i < holderArray.length; i++) {
if(holderArray[i] > 0) {
System.out.printf("number: %2d; count: %2d; sum: %2d%n", i + min, holderArray[i], (i + min) * holderArray[i]);
}
}
}
This prints out:
number: 3; count: 2; sum: 6
number: 4; count: 2; sum: 8
number: 6; count: 4; sum: 24
number: 20; count: 1; sum: 20
I don't completely understand your question, but based from what I understood, you want to get the sum of all matched numbers if its greater than 1? In that case there is a O(n) solution that you could use.
Create an empty Map then iterate over the array, and if the current number is not within the map add it to the map with value 1, if the current element is already existing in the map then just increment it's value (val++), at the end you will have a map and for each key (each distinct number) you will have the number of matched numbers from the array as the value. All you need to do is iterate over the pairs of key,val multiply each key*val then sum up the multiplied values and you get your correct total. And if you need just the number of matched, you can in another variable sum up just the vals.
So for lets say array [1,1,5,1,5,2,2,5,1], your map will something like:
{1:4, 2:2, 5:3},
and your totals:
total matches: 9
total: 23
I hope this helps!
Here is a code fragment I just wrote. This method takes in an array of integers and creates a map of each unique integer and it's count in the list. In the second part of the method, the HashMap object countOfNumbersMap is iterated and the sum of each element is printed.
private void findSumOfMatchingNumbers(int array[]) {
HashMap<Integer, Integer> countOfNumbersMap = new HashMap<>();
// Setting up the map of unique integers and it's count
for (int i = 0 ; i < array.length ; i++) {
if (countOfNumbersMap.containsKey(array[i])) {
int currentCount = countOfNumbersMap.get(array[i]);
countOfNumbersMap.put(array[i], currentCount + 1);
} else {
countOfNumbersMap.put(array[i], 1);
}
}
for (Integer integer : countOfNumbersMap.keySet()) {
int sum = countOfNumbersMap.get(integer) * integer;
System.out.println(String.format("Number = %d, Sum = %d", integer, sum));
}
}
The worst case runtime of the program is O(n).
If the range of your integers is limited, the easiest way to do this would be to create a histogram (i.e. create an array, where under index i you store the number of occurrences of the number i).
From that, it's easy to find elements that occur more than once, and sum them up. This solution has a complexity of O(n+k), where k is the range of your integers.
Another solution is to sort the array,then the matching numbers will be next to each other, and it's easy to count them. This has O(nlogn) complexity.
If these methods are not allowed, here is a solution in O(n^2) that only uses for loops:
Sum up the whole array
With a double loop, find all elements that are unique, subtract them from the sum, and count their number.
The remaining sum is the sum of all elements occurring more than once, and the count of unique elements subtracted from the length of the array gives the number of matching element.

Algorithm for sorting an array with 4 options

I was given a task to sort an array that is filled with (non negative) integers.
They should be sorted such that the output is in the following order:
Numbers where the remainder of them divided by 4 is 0
Numbers where the remainder of them divided by 4 is 1
Numbers where the remainder of them divided by 4 is 2
Numbers where the remainder of them divided by 4 is 3
I tried to write a simple algorithm that should work at O(n) (the task is to write the code efficiently).
But I think it's a mess, a few cases didn't work (for example when I tried an array where the first few numbers have remainders of 3).
Any suggestion on how to fix it or a better way of doing this?
public static void sortByFour(int[] arr)
{
int zeroR = -1, oneR = 0, twoR = arr.length-1, threeR = arr.length;
do
{
if (arr[oneR]%4==1)
oneR++;
else if (arr[oneR]%4==0)
{
zeroR++;
int temp = arr[oneR];
arr[oneR] = arr[zeroR];
arr[zeroR] = temp;
oneR++;
}
else if (arr[oneR]%4==2)
{
twoR--;
int temp = arr[oneR];
arr[oneR] = arr[twoR];
arr[twoR] = temp;
}
else if (arr[oneR]%4==3)
{
threeR--;
int temp = arr[oneR];
arr[oneR] = arr[threeR];
arr[threeR] = temp;
}
} while (oneR < threeR && oneR < twoR);
}
A bucket sort can do the trick for you. Note that you can overcome the O(n) extra space factor of bucket sort by looping 4 times (once per each reminder), something like (java-like pseudo code):
final int REMINDER = 4; //4 because you use %4
int curr = -1;
for (int r = 0; r < REMINDER ; r++) {
for (int i = curr + 1; i < arr.length; i++) {
if (arr[i] % REMINDER == r) {
//swap elements:
int temp = arr[i];;
arr[i] = arr[++curr];
arr[curr] = temp;
}
}
}
The idea is to 'remember' where you have last set element, and iterate the array 4 times, and swap elements with matching reminder to the desired location (which you remembered).
Complexity is still O(n) with O(1) extra space.
An alternative is O(n) (but much better constants) time with O(n) space is to use a classic bucket sort, with a single pass store all elements in 4 different lists according to the desired reminder, and in a 2nd pass - on those 4 lists, fill the original array with the elements according to the desired order.

Compute smaller and bigger values for an array position

I have the following problem I need to optimize. For a given array(with duplicated keys allowed), for each position i in the array, I need to compute all bigger values right of i, and all smaller values left of i. If we have:
1 1 4 3 5 6 7 and i = 3(value 3), the count of smaller values to left of i is 1(no repeated keys), and to the right, the number of bigger values is 3.
The brute force solution of this problem is ~N^2, and with some extra space I can manage to compute the smaller values from the bigger ones, so reducing complexity to ~(N^2)/2.
My question is: is there a faster way to get it done? Maybe NlgN? I imagine there is a data structure out there I don't know which will allow me to do the computation faster.
EDIT: Thank you all for your replies and discussions. You can find two good solutions two the problem below. Always a pleasure learning from developers in stackoverflow.
Here's an O(n log n) solution.
As hinted by #SayonjiNakate, the solution using segment tree (I used Fenwick tree in my implementation) runs in O(n log M) time, where M is the maximum possible value in the array.
Firstly, note that the problem "number of smaller elements on the left" is equivalent to the problem "number of greater elements on the right" by reversing and negating the array. So, in my explanation below I only describe the "number of smaller elements on the left", which I call "lesser_left_count".
Algorithm for lesser_left_count:
The idea is to be able to find the total of numbers smaller than a specific number.
Define an array tree with size upto MAX_VALUE, which will store the value 1 for seen numbers and 0 otherwise.
Then as we traverse the array, when we see a number num, just assign the value 1 to tree[num] (update operation). Then lesser_left_count for a number num is the sum from 1 to num-1 (sum operation) so far, since all smaller numbers to the left of current position would have been set to 1.
Simple right? If we use Fenwick tree, the update and sum operation can be done each in O(log M) time, where M is the maximum possible value in the array. Since we are iterating over the array, total time is O(n log M).
The only disadvantage of the naive solution is that it uses a lot of memory as M gets bigger (I set M=2^20-1 in my code, which take around 4MB of memory). This can be improved by mapping distinct integers in the array into smaller integers (in a way that preserve the order). The mapping can be done in simply O(n log n) by sorting the array. So the number M can be reinterpreted as "number of distinct elements in the array".
So the memory wouldn't be any problem anymore, because if after this improvement you indeed need huge memory, that means there are that many distinct numbers in your array, and the time complexity of O(n) will already be too high to be calculated in normal machine anyway.
For the sake of simplicity, I didn't include that improvement in my code.
Oh, and since Fenwick tree only works for positive numbers, I converted the numbers in the array to be minimum 1. Note that this doesn't change the result.
Python code:
MAX_VALUE = 2**20-1
f_arr = [0]*MAX_VALUE
def reset():
global f_arr, MAX_VALUE
f_arr[:] = [0]*MAX_VALUE
def update(idx,val):
global f_arr
while idx<MAX_VALUE:
f_arr[idx]+=val
idx += (idx & -idx)
def cnt_sum(idx):
global f_arr
result = 0
while idx > 0:
result += f_arr[idx]
idx -= (idx & -idx)
return result
def count_left_less(arr):
reset()
result = [0]*len(arr)
for idx,num in enumerate(arr):
cnt_prev = cnt_sum(num-1)
if cnt_sum(num) == cnt_prev: # If we haven't seen num before
update(num,1)
result[idx] = cnt_prev
return result
def count_left_right(arr):
arr = [x for x in arr]
min_num = min(arr)
if min_num<=0: # Got nonpositive numbers!
arr = [min_num+1+x for x in arr] # Convert to minimum 1
left = count_left_less(arr)
arr.reverse() # Reverse for greater_right_count
max_num = max(arr)
arr = [max_num+1-x for x in arr] # Negate the entries, keep minimum 1
right = count_left_less(arr)
right.reverse() # Reverse the result, to align with original array
return (left, right)
def main():
arr = [1,1,3,2,4,5,6]
(left, right) = count_left_right(arr)
print 'Array: ' + str(arr)
print 'Lesser left count: ' + str(left)
print 'Greater right cnt: ' + str(right)
if __name__=='__main__':
main()
will produce:
Original array: [1, 1, 3, 2, 4, 5, 6]
Lesser left count: [0, 0, 1, 1, 3, 4, 5]
Greater right cnt: [5, 5, 3, 3, 2, 1, 0]
or if you want Java code:
import java.util.Arrays;
class Main{
static int MAX_VALUE = 1048575;
static int[] fArr = new int[MAX_VALUE];
public static void main(String[] args){
int[] arr = new int[]{1,1,3,2,4,5,6};
System.out.println("Original array: "+toString(arr));
int[][] leftRight = lesserLeftRight(arr);
System.out.println("Lesser left count: "+toString(leftRight[0]));
System.out.println("Greater right cnt: "+toString(leftRight[1]));
}
public static String toString(int[] arr){
String result = "[";
for(int num: arr){
if(result.length()!=1){
result+=", ";
}
result+=num;
}
result+="]";
return result;
}
public static void reset(){
Arrays.fill(fArr,0);
}
public static void update(int idx, int val){
while(idx < MAX_VALUE){
fArr[idx]+=val;
idx += (idx & -idx);
}
}
public static int cntSum(int idx){
int result = 0;
while(idx > 0){
result += fArr[idx];
idx -= (idx & -idx);
}
return result;
}
public static int[] lesserLeftCount(int[] arr){
reset();
int[] result = new int[arr.length];
for(int i=0; i<arr.length; i++){
result[i] = cntSum(arr[i]-1);
if(cntSum(arr[i])==result[i]) update(arr[i],1);
}
return result;
}
public static int[][] lesserLeftRight(int[] arr){
int[] left = new int[arr.length];
int min = Integer.MAX_VALUE;
for(int i=0; i<arr.length; i++){
left[i] = arr[i];
if(min>arr[i]) min=arr[i];
}
for(int i=0; i<arr.length; i++) left[i]+=min+1;
left = lesserLeftCount(left);
int[] right = new int[arr.length];
int max = Integer.MIN_VALUE;
for(int i=0; i<arr.length; i++){
right[i] = arr[arr.length-1-i];
if(max<right[i]) max=right[i];
}
for(int i=0; i<arr.length; i++) right[i] = max+1-right[i];
right = lesserLeftCount(right);
int[] rightFinal = new int[right.length];
for(int i=0; i<right.length; i++) rightFinal[i] = right[right.length-1-i];
return new int[][]{left, rightFinal};
}
}
which will produce same result.
Try segment tree data structure used for solving RMQ.
It would give you exactly n log n.
And look through RMQ problem generally, your problem may be reduced to it.
Here's a relatively simple solution that's O(N lg(N)) that doesn't rely on the entries being among finitely many integers (in particular, it should work for any ordered data type).
We assume the output is to be stored in two arrays; lowleft[i] will at the end contain the number of distinct values x[j] with j < i and x[j] < x[i], and highright[i] will at the end contain the number of distinct values x[j] with j > i and x[j] > x[i].
Create a balanced tree data structure that maintains in each node, the number of nodes in the subtree rooted at that node. This is fairly standard, but not a part of the Java standard library I think; it's probably easiest to do an AVL tree or so. The type of the values in the nodes should be the type of the values in your array.
Now first iterate forward through the array. We start with an empty balanced tree. For every value x[i] we encounter, we enter it into the balanced tree (near the end there are O(N) entries in this tree, so this step takes O(lg(N)) time). When searching for the position to enter x[i], we keep track of the number of values less than x[i] by adding up the sizes of all left subtrees whenever we take the right subtree, and adding what will be the size of the left subtree of x[i]. We enter this number into lowleft[i].
If the value x[i] is already in the tree, we just carry on with the next iteration of this loop. If the value x[i] is not in there, we enter it and rebalance the tree, taking care to update the subtree sizes correctly.
Each iteration of this loop takes O(lg(N)) steps, for a total of O(N lg(N)). We now start with an empty tree and do the same thing iterating backward through the array, finding the position for every x[i] in the tree, and every time recording the size of all subtrees to the right of the new node as highright[i]. Total complexity therefore O(N lg(N)).
Here is an algorithm which should give you O(NlgN):
Iterate over the list once and build a map of key => indexList. So for ever key (element in the array) you store a list of all the indices where that key is in the array. This will take O(N) (iterate over the list) + N*O(1) (appending N items to lists) steps. So this step is O(N). The second step requires that these lists are sorted which they will be as we are iterating over the list from left to right so a newly inserted index in a list will always be larger than all the other ones which are already in there.
Iterate over the list again and for each element search the index lists for all keys which are larger than the current element for the first index which is after the current index. This gives you the number of elements to the right of the current one which are larger than the current element. As the index lists are sorted you can do a binary search which will take O(k * lgN) steps with k being the number of keys larger then the current one. If the number of keys has an upper limit then this is a constant as far as big-O is concerned. The second step here is to search all smaller keys and find the first index in the list which is prior to the current one. This will give you the number of element to the left of the current one which are smaller. Same reasoning as above this is O(k * lgN)
So assuming the number of keys is limited this should give you O(N) + N * 2 * O(lgN) so overall O(NlgN) if I'm not mistaken.
Edit: Pseudo code:
int[] list;
map<int => int[]> valueIndexMap;
foreach (int i = 0; i < list.length; ++i) { // N iterations
int currentElement = list[i]; // O(1)
int[] indexList = valueIndexMap[currentElement]; // O(1)
indexList.Append(i); // O(1)
}
foreach (int i = 0; i < list.length; ++i) { // N iterations
int currentElement = list[i]; // O(1)
int numElementsLargerToTheRight;
int numElementsSmallerToTheLeft;
foreach (int k = currentElement + 1; k < maxKeys; ++k) { // k iterations with k being const
int[] indexList = valueIndexMap[k]; // O(1)
int firstIndexBiggerThanCurrent = indexList.BinaryFindFirstEntryLargerThan(i); // O(lgN)
numElementsLargerToTheRight += indexList.Length - firstIndexBiggerThanCurrent; // O(1)
}
foreach (int k = currentElement - 1; k >= 0; --k) { // k iterations with k being const
int[] indexList = valueIndexMap[k]; // O(1)
int lastIndexSmallerThanCurrent = indexList.BinaryFindLastEntrySmallerThan(i); // O(lgN)
numElementsSmallerToTheLeft += lastIndexSmallerThanCurrent; // O(1)
}
}
Update: I tinkered around with a C# implementation in case anyone is interested;

Find the largest sum including at most two consecutive elements from an array

I've been playing around a bit with the algorithms for getting the largest sum with no two adjacent elements in an array but I was thinking:
If we have an array with n elements and we want to find the largest sum so that 3 elements never touch. That's to say if we have the array a = [2, 5, 3, 7, 8, 1] we can pick 2 and 5 but not 2, 5 and 3 because then we have 3 in a row. The larget sum with these rules for this array would be: 22 (2 and 5, 7 and 8. 2+5+7+8=22)
I'm not sure how I would implement this, any ideas?
Edit:
I've only come so far as to think about what might be good to do:
Let's just stick to the same array:
int[] a = {2, 5, 3, 7, 8, 1};
int{} b = new int[n}; //an array to store results in
int n = a.length;
// base case
b[1] = a[1];
// go through each element:
for(int i = 1; i < n; i++)
{
/* find each possible way of going to the next element
use Math.max to take the "better" option to store in the array b*/
}
return b[n]; // return the last (biggest) element.
This is just a thought I got in my head, hasn't reached longer than this.
Algorithm for Maximum sum such that no two elements are adjacent:
Loop for all elements in arr[] and maintain two sums incl and excl where incl = Max sum including the previous element and excl = Max sum excluding the previous element.
Max sum excluding the current element will be max(incl, excl) and max sum including the current element will be excl + current element (Note that only excl is considered because elements cannot be adjacent).
At the end of the loop return max of incl and excl.
Implementation:
#include<stdio.h>
/*Function to return max sum such that no two elements
are adjacent */
int FindMaxSum(int arr[], int n)
{
int incl = arr[0];
int excl = 0;
int excl_new;
int i;
for (i = 1; i < n; i++)
{
/* current max excluding i */
excl_new = (incl > excl)? incl: excl;
/* current max including i */
incl = excl + arr[i];
excl = excl_new;
}
/* return max of incl and excl */
return ((incl > excl)? incl : excl);
}
/* Driver program to test above function */
int main()
{
int arr[] = {5, 5, 10, 100, 10, 5};
printf("%d \n", FindMaxSum(arr, 6));
getchar();
return 0;
}
Time Complexity: O(n)
Space Complexity: O(1)
Edit 1:
If you understand the above code, we can easily do this problem by maintaining the count of already adjacent numbers for previous position.
Here is a working implementation to the required question
//We could assume we store optimal result upto i in array sum
//but we need only sum[i-3] to sum[i-1] to calculate sum[i]
//so in this code, I have instead maintained 3 ints
//So that space complexity to O(1) remains
#include<stdio.h>
int max(int a,int b)
{
if(a>b)
return 1;
else
return 0;
}
/*Function to return max sum such that no three elements
are adjacent */
int FindMaxSum(int arr[], int n)
{
int a1 = arr[0]+arr[1];//equivalent to sum[i-1]
int a2 =arr[0];//equivalent to sum[i-2]
int a3 = 0;//equivalent to sum [i-3]
int count=2;
int crr = 0;//current maximum, equivalent to sum[i]
int i;
int temp;
for (i = 2; i < n; i++)
{
if(count==2)//two elements were consecutive for sum[i-1]
{
temp=max(a2+arr[i],a1);
if(temp==1)
{
crr= a2+arr[i];
count = 1;
}
else
{
crr=a1;
count = 0;
}
//below is the case if we sould have rejected arr[i-2]
// to include arr[i-1],arr[i]
if(crr<(a3+arr[i-1]+arr[i]))
{
count=2;
crr=a3+arr[i-1]+arr[i];
}
}
else//case when we have count<2, obviously add the number
{
crr=a1+arr[i];
count++;
}
a3=a2;
a2=a1;
a1=crr;
}
return crr;
}
/* Driver program to test above function */
int main()
{
int arr[] = {2, 5, 3, 7, 8, 1};
printf("%d \n", FindMaxSum(arr, 6));
return 0;
}
Time Complexity: O(n)
Space Complexity: O(1)
adi's solution can be easily generalized to allow up to n adjacent elements to be included in the sum. The trick is to maintain an array of n + 1 elements, where the k-th element in the array (0 ≤ k ≤ n) gives the maximum sum assuming that the k previous inputs are included in the sum and the k+1-th isn't:
/**
* Find maximum sum of elements in the input array, with at most n adjacent
* elements included in the sum.
*/
public static int maxSum (int input[], int n) {
int sums[] = new int[n+1]; // new int[] fills the array with zeros
int max = 0;
for (int x: input) {
int newMax = max;
// update sums[k] for k > 0 by adding x to the old sums[k-1]
// (loop from top down to avoid overwriting sums[k-1] too soon)
for (int k = n; k > 0; k--) {
sums[k] = sums[k-1] + x;
if (sums[k] > newMax) newMax = sums[k];
}
sums[0] = max; // update sums[0] to best sum possible if x is excluded
max = newMax; // update maximum sum possible so far
}
return max;
}
Like adi's solution, this one also runs in linear time (to be exact, O(mn), where m is the length of the input and n is the maximum number of adjacent elements allowed in the sum) and uses a constant amount of memory independent of the input length (O(n)). In fact, it could even be easily modified to process input streams whose length is not known in advance.
I would imagine putting the array into a binary tree in that order. That way you can keep track of which element is next to each other. Then just simply do an if (node is not directly linked to each other) to sum the nodes which are not next to each other. You can potentially do it with recursion and return the maximum number, makes things easier to code. Hope it helps.
For a set with n entries, there are 2^n ways to partition it. So to generate all possible sets, just loop from 0:2^n-1 and pick the elements from the array with those entries set to 1 (bear with me; I'm getting to your question):
max = 0;
for (i = 0; i < 1<<n; ++i) {
sum = 0;
for (j = 0; j < n; ++j) {
if (i & (1<<j)) { sum += array[j]; }
}
if (sum > max) { /* store max and store i */ }
}
This will find the maximum way to sum the entries of an array. Now, the issue you want is that you don't want to allow all values of i - specifically those that contain 3 consecutive 1's. This can be done by testing if the number 7 (b111) is available at any bit-shift:
for (i = 0; i < 1<<n; ++i) {
for (j = 0; j < n-2; ++j) {
if ((i & (7 << j)) == (7 << j)) { /* skip this i */ }
}
...

Categories