I got an interview question and my algorithm only pass given example test cases, and didn't pass all test cases.
Question: Given a sorted integer array, return sum of array so that each element is unique by adding some numbers to duplicate elements so that sum of unique elements is minimum.
I.e., if all elements in the array are unique, return the sum.
If some elements are duplicates, then increment them to make sure all elements are unique so that the sum of these unique elements is minimum.
Some examples:
input1[] = { 2, 3, 4, 5 } => return 19 = 2+3+4+5 (all elements are unique, so just add them up)
input2[] = { 1, 2, 2 } => return 6 = 1+2+3 (index 2 is duplicate, so increment it)
input3[] = { 2, 2, 4, 5 } => return 14 = 2+3+4+5 (index 1 is duplicate, so increment it)
These three are examples in the question, my simple algorithm is as follows and passed the given three examples, but didn't pass other cases where I couldn't see the inputs.
static int minUniqueSum(int[] A) {
int n = A.length;
int sum = A[0];
int prev = A[0];
for( int i = 1; i < n; i++ ) {
int curr = A[i];
if( prev == curr ) {
curr = curr+1;
sum += curr;
}
else {
sum += curr;
}
prev = curr;
}
return sum;
}
I couldn't see other inputs which this algorithm failed.
What I can think of other input examples are
{1, 1, 1, 1} --> {1, 2, 3, 4}
{1, 1, 2, 2, 3, 3, 3} --> {1, 2, 3, 4, 5, 6, 7}
{1, 2, 4, 4, 7, 7, 8} --> I think this should be {1, 2, 3, 4, 6, 7, 8} and my algorithm fails in this example because my algorithm has {1, 2, 4, 5, 7, 8, 9} whose sum is not minimum
What are some other test cases and an algorithm which can pass all cases?
Some people are complaining that the question is not clear. I'd like to let you know about the problem. There was no clear description about the added number if it will be allowed only positive or positive and negative. Given three examples with input and output, and some others input and output cases which you are not allowed to see, write a program to pass all other unseen input / output cases as well. That was the question.
Your algorithm will fail in cases with more repeated values, for example
2, 2, 2
You'd get 7 instead of 9.
A minimal fix using your algorithm would be:
static int minUniqueSum(int[] A) {
int n = A.length;
int sum = A[0];
int prev = A[0];
for( int i = 1; i < n; i++ ) {
int curr = A[i];
if( prev >= curr ) {
curr = prev+1;
}
sum += curr;
prev = curr;
}
return sum;
}
*As pointed out in the comments, no need to sort an already sorted array.
In JavaScript
var list = [1, 1, 1, 10, 3, 2];
function minUniqueSum(arr) {
const temp = arr.reduce((acc, cur) => {
while (acc.includes(cur)) cur++;
acc.push(cur);
return acc;
}, []);
console.log(temp); // [1, 2, 3, 10, 4, 5]
return temp.reduce((acc, cur) => acc + cur, 0);
}
var result = minUniqueSum(list);
console.log(result); // 25
I did like this, without sort.
// Complete the getMinimumUniqueSum function below.
static int getMinimumUniqueSum(int[] arr) {
int sum = 0;
ArrayList < Integer > arrayList = new ArrayList < Integer > (arr.length);
arrayList.add(arr[0]);
for (int i = 1; i < arr.length; i++) {
int val = arr[i];
while (arrayList.contains(val)) {
val++;
}
arrayList.add(val);
}
for (int i = 0; i < arrayList.size(); i++) {
sum += arrayList.get(i);
}
return sum;
}
And it passed all (13) test cases.
While this solution is based on java the thought process can be applied everywhere.
Your solution is nearly correct and optimized. Using multiple for loops will slow things down A LOT and thus should be avoided if possible! Since your array is already pre-sorted you have enough with 1 for loop.
Your assumption that you had the last test case wrong does not seem to be correct since increment means you can only do +1 (and indeed most questions limit this assignment to increments only.)
What you missed was the max range of the Integers.
If they pass a Integer.MAX_VALUE, your sum will overflow and be negative instead. So your sum variable needs to be of a bigger type. double or BigInteger should work (BigInteger would be best).
Also when they pass MAX_VALUE twice your curr+1 will also overflow becoming negative instead. So you want your curr and prev to also be a larger type. long should do for this.
public static double calculateMinSumSorted(int[] input){
double sum = input[0];
long prev = input[0];
long cur;
for(int i = 1 ; i < input.length ; i++){
cur = input[i];
if(cur <= prev){
cur = ++prev;
}
prev = cur;
sum += cur;
}
return sum;
}
Here is some of the test cases I used:
#Test
public void testSimpleArray(){
double test1 = muas.calculateMinSumSorted(new int[]{1,2,3,4});
Assert.assertEquals(10, test1, 0.1);
}
#Test
public void testBeginningSameValues(){
double test1 = muas.calculateMinSumSorted(new int[]{2,2,3,4});
Assert.assertEquals(14, test1, 0.1);
}
#Test
public void testEndingSameValues(){
double test1 = muas.calculateMinSumSorted(new int[]{1,2,4,4});
Assert.assertEquals(12, test1, 0.1);
}
#Test
public void testAllSameValues(){
double test1 = muas.calculateMinSumSorted(new int[]{1,1,1,1});
Assert.assertEquals(10, test1, 0.1);
}
#Test
public void testOverMaxIntResult(){
double test1 = muas.calculateMinSumSorted(new int[]{1,2,3,3,4,4,4,4,4,Integer.MAX_VALUE});
System.out.println(test1);
Assert.assertEquals(2147483692.0, test1, 0.1);
}
#Test
public void testDoubleMaxIntArray(){
double test1 = muas.calculateMinSumSorted(new int[]{2,2,3,4,5,6,7,8,9, Integer.MAX_VALUE, Integer.MAX_VALUE});
Assert.assertEquals(4294967349.0, test1, 0.1);
}
#Test
public void testDoubleMinIntArray(){
double test1 = muas.calculateMinSumSorted(new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE,2,2,3,4,5,6,7,8,9});
Assert.assertEquals(-4294967241.0, test1, 0.1);
}
// 1,1,2,3 -> 1,2,2,3 -> 1,2,3,3 -> 1,2,3,4 => 10
// 2,2,2 -> 2,3,2 -> 2,3,3 -> 2,3,4 => 9
public int calculateMinSumSorted(int[] input) {
int sum = input[0];
for (int i = 1, v = sum; i < input.length; v = input[i++]) {
if (input[i] <= input[i - 1]) {
input[i--] = ++v;
} else {
sum += input[i];
}
}
return sum;
}
It you're allowed to add negative values to any of the inputs then the minimum is just the Nth triangular number where N is the number of elements in the array. (I'm presuming that we're dealing only with positive numbers for the adjusted array since we could make it arbitrarily small (negative) otherwise.
So you're algorithm is just a matter of looking for a pair of consecutive values that are the same. If not found return the sum else N * (N + 1) / 2.
If instead it's true that only duplicate elements can be adjusted, then the approach is to find holes between consecutive elements and fill them up with previously "queued" values. The actual values of the "queued" elements is irrelevant and only a counter is necessary. Below is a C# solution where I'm assuming that the adjustments to elements must be positive values. So that means we can't go backward and fill unused holes which simplifies the problem.
int F()
{
int[] a = {2, 2, 2, 3, 8, 9}; // sorted list
int n = 0; /* num */ int p = 0; /* prev; zero is an invalid value in the list */
int q = 0; /* queue */ int s = 0; /* sum */
for (int i = 1; i < a.Length; i++)
{
n = a[i];
if (n == p)
q++; // increment queue on duplicate number
else
{
// for every hole between array values, decrement queue and add to the sum
for (int j = 1; q > 0 && j < n - p; j++, q--)
s += p + j;
s += (p = n);
}
}
// flush the queue
for (; q > 0; q--)
s += ++n;
return s;
}
You example {1, 2, 4, 4, 7, 7, 8} suggests that the previous assumption is not valid. So I went ahead and wrote a version that uses a queue to store skipped holes for later filling. It's not really that painful, and it's very similar in structure, but it might still be too much for most interviews.
using System.Collections.Generic;
int F2()
{
int[] a = {1, 1, 8, 8, 8, 8, 8}; // sorted list
int n = 0; /* num */ int p = 0; // prev; zero is an invalid value in the list
int q = 0; /* queue */ int s = 0; // sum
Queue<int> h = new Queue<int>(); // holes
for (int i = 1; i < a.Length; i++)
{
n = a[i];
if (n == p)
q++; // increment queue on duplicate number
else
{
for (int j = 1; j < n - p; j++)
if (h.Count <= q + a.Length - i) // optimization
h.Enqueue(p + j);
s += (p = n);
}
}
// flush the queue
for (; q > 0; q--)
s += h.Count > 0 ? h.Dequeue() : ++n;
return s;
}
Try them both out here: http://rextester.com/APO79723
int a[] = {1,2,2,3,5,6,6,6,6 }; So what would be elements in array for sum
As per above problem statement it would be {1,2,3,4,5,6,7,8,9 }
Solution
public static void uniqueSum(){
int a[] = {1,2,2,3,5,6,6,6,6 };
int n = a.length;
int sum = a[0];
int prv=a[0];
for(int i=1; i<n;i++){
int cur = a[i];
if(cur==prv){
cur = cur+1;
sum+= cur;
System.out.print("--"+cur);
}else{
if(cur<prv){
cur = prv +1;
}
sum += cur;
}
prv = cur;
}
System.out.println("===================== "+sum);
}
You can try the below code.
int a[] = {1, 1 , 1};
ArrayList<Integer> arr = new ArrayList<Integer>();
HashMap hash = new HashMap();
for(int i=0;i<a.length;i++){
arr.add(a[i]);
}
int sum = 0;
hash.put(0, arr.get(0));
sum = (int) hash.get(0);
for(int i=1;i<arr.size();i++){
for(int j=1;j<=a.length;j++){
if(hash.containsValue((arr.get(i)))){
arr.set(i, arr.get(i)+1);
}else{
hash.put(i, arr.get(i));
sum += (int) hash.get(i);
break;
}
}
}
System.out.println(sum);
PS: Even I got this question in my interview, the above code passed all the test cases.
public static int minSum(int arr[]){
for(int i=0; i<arr.length-1;i++){
if(arr[i]==arr[i+1]){
arr[i+1]= arr[i+1]+1;
}
}
int sum=0;
for(int i=0; i<arr.length;i++){
sum=sum+arr[i];
}
System.out.println("sum: "+sum);
return sum;
}
Going by your description of hidden I/O, it's probably a HackerRank Test question. A better way to state the problem is "Given a sorted array of numbers, make the numbers distinct by incrementing them (num++ at a time) in such a way that the array sum is minimized."
The problem allows only increments i.e. increase a number by 1 at a time. This also ensures that the array always remains sorted.
so {1, 2, 4, 4, 7, 7, 8} --> {1, 2, 4, 5, 7, 8, 9}
Here is the problem with the solution.
https://www.geeksforgeeks.org/making-elements-distinct-sorted-array-minimum-increments/
Working solution (JAVA 7)
:
public static int getMinimumUniqueSum(List <Integer> arr){
int sum = 0, val = 0;
ArrayList < Integer > arrayList = new ArrayList < Integer > (arr.size());
arrayList.add(arr.get(0));
for (int i = 1; i < arr.size(); i++) {
val = arr.get(i);
while (arrayList.contains(val)) {
val++;
}
arrayList.add(val);
}
for (int i = 0; i < arrayList.size(); i++) {
sum += arrayList.get(i);
}
return sum;
}
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
/* No sorting required. Works even with random list */
public static int getMinUniqueSum(List<Integer> list)
{
Set<Integer> set = new HashSet<Integer>();
int sum = 0;
for (Integer val : list)
{
if(!set.add(val))
{
while(true)
{
Integer temp = val + 1;
if(set.add(temp))
{
sum = sum + temp;
break;
}
}
}
else
{
sum = sum + val;
}
}
return sum;
}
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
System.out.println("Enter size of the list");
int n = s.nextInt();
List<Integer> list = new ArrayList<Integer>(n);
System.out.println("Enter " + n + " elements of the list");
for(int i = 0; i < n; i++)
list.add(s.nextInt());
s.close();
System.out.println("MinUniqueSum = " + getMinUniqueSum(list));
}
}
In C++:
int64_t getMinimumUniqueSum(std::vector<int> arr)
{
std::sort(arr.begin(), arr.end());
int64_t sum = arr[0];
size_t i = 0;
size_t j = 1;
size_t gap_i = j;
int avail_val = arr[j] + 1;
while (j < arr.size()) {
// find next gap with available values
if (j > gap_i) {
gap_i = j;
avail_val = arr[gap_i] + 1;
}
while (gap_i < arr.size() && arr[gap_i] <= avail_val) {
avail_val = arr[gap_i] + 1;
gap_i++;
}
if (arr[i] == arr[j]) {
// update duplicated value
arr[j] = avail_val;
avail_val++;
} else {
// move index of prev value - i
i = j;
}
sum += arr[j];
j++;
}
return sum;
}
Straightforward solution that uses hash set would be mush slower:
int64_t getMinimumUniqueSum_Slow(std::vector<int> arr)
{
std::unordered_set<int> s;
int64_t sum = 0;
for (int a : arr) {
while (s.find(a) != s.end()) {
a++;
}
s.insert(a);
sum += a;
}
return sum;
}
Slow version takes about 10s to process array with 10^5 numbers.
While optimized one takes about 0.5s to process array with 10^7 numbers.
But while the slow solution is obviously correct - we can use it for testing of the optimized one:
std::vector<int> random_vec(size_t size, int min_val, int max_val)
{
std::random_device rnd_device;
std::mt19937 mersenne_engine {rnd_device()};
std::uniform_int_distribution<int> dist {min_val, max_val};
auto gen = [&dist, &mersenne_engine](){
return dist(mersenne_engine);
};
std::vector<int> arr(size);
generate(begin(arr), end(arr), gen);
return arr;
}
int main()
{
for (int i = 0; i < 1000; i++) {
printf("%d\n", i);
auto arr = random_vec(i*10+1, -5, 5);
int64_t x = getMinimumUniqueSum(arr);
int64_t y = getMinimumUniqueSum_Slow(arr);
if (x != y) {
printf("Results not match: fast -> %lld, slow -> %lld !!!\n\n", x, y);
return 1;
}
}
return 0;
}
In Haskell:
countdups _ _ [] = []
countdups first prev (x:xs)
| (prev >= x) && (first /= True) = (prev+1) : countdups False (prev+1) xs
| otherwise = x: countdups False x xs
minsum list = sum $ countdups True 0 (sort list)
Here is some of the test cases I used:
countdups True 0 [2, 3, 4, 5]
[2,3,4,5]
minsum = 14
countdups True 0 [1, 2, 2]
[1,2,3]
minsum = 6
countdups True 0 [2, 2, 4, 5]
[2,3,4,5]
minsum = 14
countdups True 0 [1,2,2,3,7]
[1,2,3,4,7]
minsum = 17
countdups True 0 [1,1,1,2,3,10]
[1,2,3,4,5,10]
minsum = 25
countdups True 0 [1,1,1,1]
[1,2,3,4]
minsum= 10
countdups True 0 [1,2,3,3,4,4,4,4,4,2147483647]
[1,2,3,4,5,6,7,8,9,2147483647]
minsum= 2147483692
Using Collections in java helps a lot ,
Here i use HashMap as it stores values for every Unique Key
My Key in hashmap is the array elements and the value is the no. of counts as it appears in the array.
package uniquesum;
import java.util.*;
public class Uniquesum {
static HashMap<Integer, Integer> hp = new HashMap<Integer, Integer>();
static int Sum(int arr[]){
int sum=0;
Arrays.sort(arr);
hp.put(arr[0], 1);
for(int i=1; i<arr.length; i++){
if(hp.containsKey(arr[i])){
Integer val = hp.get(arr[i]);
hp.put(arr[i], val+1);
hp.put(arr[i]+val, 1);
}
else{
hp.put(arr[i], 1);
}
}
for(Map.Entry m:hp.entrySet()){
sum = sum + (int)m.getKey();
}
return sum;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int arr[] = new int [n];
for(int i=0; i<n;i++){
arr[i] = scan.nextInt();
}
System.out.println("Sum is " + Sum(arr));
}
}
Related
Given an array C of size N-1 and given that there are numbers from 1 to N with one element missing, the missing number is to be found.
Input:
The first line of input contains an integer T denoting the number of test cases. For each test case first line contains N(size of array). The subsequent line contains N-1 array elements.
Output:
Print the missing number in array.
This problem is to find the missing number in a series of n integers. but, while using the below code I could not get the output as expected.
#include <stdio.h>
int main()
{
//code
int T,run,i;
scanf("%d", &T);
long N,res,C,en;
long arra[1];
for (run = 0;run <T; run++ )
{
long arra[T];
scanf("%ld", &N);
res =0;
for (i = 0; i <N-1; i++)
{
scanf("%ld",&C);
res = res + C;
}
en = ((N*(N+1))/2)- res; // subtracting the overall sum of array elements from N integers
arra[run]=en; //saving it to array
}
for(run = 0; run < T; run++)
{
printf("%ld ",arra[run]);
}
return 0;
}
I expected the below input and output:
Input:
2
5
1 2 3 5
10
1 2 3 4 5 6 7 8 10
Output:
4
9
but actual output is
1 -8719623343620674816
You re-declared the variable arra inside the for loop. So when you assign to arra[run], you're assigning to the inner array, not the one in the main() function. So you get garbage when you try to print the contents of the array at the end.
You also declared the first array with only one element, rather than T elements.
Get rid of the second declaration, and change the first one from
long arra[1];
to
long arra[T];
correct code
declare the arra before the for loop else for every iteration the arra will be re declared deleting the previous values in it
#include <stdio.h>
int main()
{
//code
int T,run,i;
scanf("%d", &T);
long N,res,C,en;
long arra[T];
for (run = 0;run <T; run++ )
{
scanf("%ld", &N);
res =0;
for (i = 0; i <N-1; i++)
{
scanf("%ld",&C);
res = res + C;
}
en = ((N*(N+1))/2)- res; // subtracting the overall sum of array elements from N integers
arra[run]=en; //saving it to array
}
for(run = 0; run < T; run++)
{
printf("%ld ",arra[run]);
}
return 0;
}
here is a self explainable simple example
public static void Main()
{
int[] ary = { 5, 11, 3, 7, 13, 15 }; //{ 3, 5, 7, 11, 13, 15 };
Array.Sort(ary);
int freq=0;
int minval = ary[0];
int[] correct = new int[ary.Length+1];
int[,] freqs = new int[(ary.Length), 2];
freqs[0, 0] = 1;
freqs[0, 1] = ary[0];
for (int i = 0; i < ary.Length-1; i++)
{
int dif = ary[i + 1] - ary[i];
int res = Search(freqs, dif);
if (res < 0)
{
freqs[i, 0] = 1;
freqs[i, 1] = dif;
}
else
{
freqs[res, 0] = freqs[res, 0] + 1;
}
};
for (int i = 0; i < freqs.GetLength(0); i++)
{
freq =freqs[i, 0] >freq? freqs[i, 1] : freq;
}
for (int i = 0; i < correct.Length;i++)
{
correct[i] = i == 0 ? minval :( correct[i - 1] + freq);
}
foreach (int i in correct.Except(ary))
{
Console.WriteLine("eksik değer="+i);
}
Console.ReadLine();
int Search(int[,] matrix, int val)
{
int hit = -99;
for (int i = 0; i < matrix.GetLength(0); i++)
{
if (val == matrix[i, 1])
return i;
}
return hit;
}
}
DISCLAIMER:
Described problem looks like a task from a competition. I'm not participating in any of them, I'm not aware about any ongoing competitions, which might involve the problem. If there are any of them, I'll close the question to stay fair!
I have a problem:
given an array A of values and integer K, split A into exactly K non-overlapping contiguous subarrays in such way that difference between a subarray with minimal and a subarray maximum sums is minimal. It is allowed to rotate A by any number in any direction.
Consider an example:
Input: A = [5 1 1 1 3 2], K = 3
Output: [5][1 1 1][3 2], maximum sum = 5, minimum sum = 3, result = 2
I have partially working code (terribly ugly, my bad, but it does not meant to be production quality):
#include <climits>
#include <cstdio>
#include <cstring>
const int max_n = 50;
const int max_k = 20;
int deps[max_n];
int max (int x, int y) {
return x > y ? x : y;
}
int min (int x, int y) {
return x < y ? x : y;
}
int sum (int a[], int start, int end) {
int res = 0;
for (int i = start; i <= end; ++i) res += a[i];
return res;
}
int k_partitioning(int k, int n, int deps[]) {
int res = INT_MAX;
// consider all possible rotations/shifts
for(int offset = 0; offset < n; ++offset) {
for(int l_min = 0; l_min < n; ++l_min) {
for(int r_min = l_min; r_min < n; ++r_min) {
// check minimal sum subarray
int min_sum = sum (deps, l_min, r_min);
int dp[k][n];
for (int s = 0; s < k; ++s) {
for (int q = 0; q < n; ++q) {
dp[s][q] = 0;
}
}
// assuming that current sum is a target sum
dp[0][r_min-l_min] = min_sum;
for(int p = 1; p < k; ++p) {
for(int l_max = 0; l_max < n; ++l_max) {
for(int r_max = 0; r_max < n; ++r_max) {
int max_sum = sum(deps, l_max, r_max);
if (max_sum >= min_sum) dp[p][r_max] = max(dp[p-1][l_max], max_sum);
} // l_maxs
} // r_maxs
} // partitions
// printing dp
// skip incorrect partitioning, when not all K partitions were used
if (dp[k-1][n-1] == 0) continue;
// update difference
res = min (res, dp[k-1][n-1] - min_sum);
} // end min sum seg
} // start min sum seg
//break;
} // cuts
return res;
}
int main(int argc, char* argv[]) {
int k = 0;
scanf("%d", &k);
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &deps[i]);
}
printf ("%d\n", k_partitioning(k, n, deps));
return 0;
}
The idea is simple: assume that current partition has minimal sum, enumerate all possible maximal partitions, setup dynamic programming for generating maximum sum with minimal value, check for difference. Total complexity: O(K*N^4).
My problem is that it fails some tests and I'm stuck with troubleshooting it. Could someone help me with it?
Failed test, for example:
N = 4, K = 2, A = [6 13 10 2]
UPDATE
This version should fix some previous issues. First, it removes wasteful loop over "offsets" and adds just an array rotation in the end of l_min loop. Second, I've noticed, that dp can't be initialized with 0 - this is minimization task, so it should be initialized with some large value (depends on a problem's constants, max_value here already is out of value domain). Finally, intervals should not overlap anymore - each sum exclude left end of an interval. However, it still does not produce expected results.
#include <climits>
#include <cstdio>
#include <cstring>
const int max_value = 200000;
const int max_n = 50;
const int max_k = 20;
int deps[max_n];
int max (int x, int y) {
return x > y ? x : y;
}
int min (int x, int y) {
return x < y ? x : y;
}
int sum (int a[], int start, int end) {
int res = 0;
for (int i = start; i <= end; ++i) res += a[i];
return res;
}
int k_partitioning(int k, int n, int deps[]) {
int res = max_value;
for(int l_min = 0; l_min < n; ++l_min) {
for(int r_min = l_min; r_min < n; ++r_min) {
int min_sum = sum (deps, l_min+1, r_min);
int dp[k][n];
for (int s = 0; s < k; ++s) {
for (int q = 0; q < n; ++q) {
dp[s][q] = max_value;
}
}
// assuming that current sum is a target sum
dp[0][r_min-l_min] = min_sum;
for(int p = 1; p < k; ++p) {
for(int l_max = 0; l_max < n; ++l_max) {
for(int r_max = l_max; r_max < n; ++r_max) {
int max_sum = sum(deps, l_max+1, r_max);
if (max_sum >= min_sum) dp[p][r_max] = max(dp[p-1][l_max], max_sum);
} // l_maxs
} // r_maxs
} // partitions
// skip incorrect partitioning, when not all K partitions were used
if (dp[k-1][n-1] == max_value) continue;
// update difference
res = min (res, dp[k-1][n-1] - min_sum);
} // end min sum seg
// rotate an array to consider different starting points
int tmp[n];
for (int i = 0; i < n; ++i) {
int new_idx = i + n + 1;
tmp[new_idx % n] = deps[i];
}
for(int i = 0; i < n; ++i) deps[i] = tmp[i];
} // start min sum seg
return res;
}
int main(int argc, char* argv[]) {
int k = 0;
scanf("%d", &k);
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &deps[i]);
}
printf ("%d\n", k_partitioning(k, n, deps));
return 0;
}
Ok, I think I did it!
The idea is following: we assume that minimum sum interval always starts from 0. Then we start to enumerate maximum sum intervals, starting from the right boundary of the minimal interval. We build DP problem for current max interval to determine a minimum maximal sum. After that you update result and rotate an array by one.
My code is not perfect in a way that I compute current sums each iteration. One can pre-compute them and just index them each time.
This code might have some bugs, but it passes all test that I have.
#include <climits>
#include <cstdio>
#include <cstring>
const int max_value = 200000;
const int max_n = 50;
const int max_k = 20;
int deps[max_n];
int max (int x, int y) {
return x > y ? x : y;
}
int min (int x, int y) {
return x < y ? x : y;
}
int sum (int a[], int start, int end) {
int res = 0;
for (int i = start; i <= end; ++i) res += a[i];
return res;
}
int k_partitioning(int k, int n, int deps[]) {
int res = max_value;
for(int offset = 0; offset < n; ++offset) {
int l_min = 0;
for(int r_min = l_min; r_min < n; ++r_min) {
int min_sum = sum (deps, l_min, r_min);
int dp[k][n];
for (int s = 0; s < k; ++s) {
for (int q = 0; q < n; ++q) {
dp[s][q] = max_value;
}
}
// assuming that current sum is a target sum
dp[0][r_min-l_min] = min_sum;
for(int p = 1; p < k; ++p) {
for(int l_max = r_min; l_max < n; ++l_max) {
for(int r_max = l_max; r_max < n; ++r_max) {
int max_sum = sum(deps, l_max+1, r_max);
if (max_sum >= min_sum) {
dp[p][r_max] = min(dp[p][r_max], max(dp[p-1][l_max], max_sum));
}
} // l_maxs
} // r_maxs
} // partitions
// skip incorrect partitioning, when not all K partitions were used
if (dp[k-1][n-1] == max_value) continue;
// update difference
res = min (res, dp[k-1][n-1] - min_sum);
} // end min sum seg
int tmp[n];
for (int i = 0; i < n; ++i) {
int new_idx = i + n - 1;
tmp[new_idx % n] = deps[i];
}
for(int i = 0; i < n; ++i) deps[i] = tmp[i];
} // start min sum seg
return res;
}
int main(int argc, char* argv[]) {
int k = 0;
scanf("%d", &k);
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &deps[i]);
}
printf ("%d\n", k_partitioning(k, n, deps));
return 0;
}
Solution without rotations:
1) Compute max M and total S of the array - O(n)
2) Let there be a function F(P), which returns True if it is possible to get a Sum P or less with k (>= 0) partitions still remaining.
3) Do a binary search on range(M, S) using F. - O(log(S-M))
4) Logic behind F: Fill a bucket till it's not greater than S/K. Then move onto next bucket. If there are still items remaining and no buckets remaining, then the answer is false - O(n)
Time Complexity = O(n) + O(n) * (log(S-M)) = O(n*log(S-M))
Solution with Rotations:
For all rotations in [0, 1, ... N-1], compute min sum.
Total Time Complexity = O(n) * O(nlog(S-M)) = O(n^2*log(S-M))
Now that you've got your code working, here's an alternative method :)
Consider that for each k, we can pair a sum growing from A[i] to the left (sum A[i-j..i]) with all available intervals recorded for f(k-1, i-j-1) and update them - for each interval, (low, high), if the sum is greater than high, then new_interval = (low, sum) and if the sum is lower than low, then new_interval = (sum, high); otherwise, the interval stays the same. For example,
i: 0 1 2 3 4 5
A: [5 1 1 1 3 2]
k = 3
i = 3, j = 0
The ordered intervals available for f(3-1, 3-0-1) = f(2,2) are:
(2,5), (1,6) // These were the sums, (A[1..2], A[0]) and (A[2], A[0..1])
Sum = A[3..3-0] = 1
Update intervals: (2,5) -> (1,5)
(1,6) -> (1,6) no change
Now, we can make this iteration much more efficient by recognizing and pruning intervals during the previous k round.
Watch:
A: [5 1 1 1 3 2]
K = 1:
N = 0..5; Intervals: (5,5), (6,6), (7,7), (8,8), (11,11), (13,13)
K = 2:
N = 0: Intervals: N/A
N = 1: Intervals: (1,5)
N = 2: (1,6), (2,5)
Prune: remove (1,6) since any sum <= 1 would be better paired with (2,5)
and any sum >= 6 would be better paired with (2,5)
N = 3: (1,7), (2,6), (3,5)
Prune: remove (2,6) and (1,7)
N = 4: (3,8), (4,7), (5,6), (5,6)
Prune: remove (3,8) and (4,7)
N = 5: (2,11), (5,8), (6,7)
Prune: remove (2,11) and (5,8)
For k = 2, we are now left with the following pruned record:
{
k: 2,
n: {
1: (1,5),
2: (2,5),
3: (3,5),
4: (5,6),
5: (6,7)
}
}
We've cut down the iteration of k = 3 from a list of n choose 2 possible splits to n relevant splits!
The general algorithm applied to k = 3:
for k' = 1 to k
for sum A[i-j..i], for i <- [k'-1..n], j <- [0..i-k'+1]:
for interval in record[k'-1][i-j-1]: // records are for [k'][n']
update interval
prune intervals in k'
k' = 3
i = 2
sum = 1, record[2][1] = (1,5) -> no change
i = 3
// sums are accumulating right to left starting from A[i]
sum = 1, record[2][2] = (2,5) -> (1,5)
sum = 2, record[2][1] = (1,5) -> no change
i = 4
sum = 3, record[2][3] = (3,5) -> no change
sum = 4, record[2][2] = (2,5) -> no change
sum = 5, record[2][1] = (1,5) -> no change
i = 5
sum = 2, record[2][4] = (5,6) -> (2,6)
sum = 5, record[2][3] = (3,5) -> no change
sum = 6, record[2][2] = (2,5) -> (2,6)
sum = 7, record[2][1] = (1,5) -> (1,7)
The answer is 5 paired with record[2][3] = (3,5), yielding the updated interval, (3,5). I'll leave the pruning logic for the reader to work out. If we wanted to continue, here's the pruned list for k = 3
{
k: 3
n: {
2: (1,5),
3: (1,5),
4: (3,5),
5: (3,5)
}
}
I finally solved this question : Split array into three subarrays, It may help you.
here I'm splitting a array into three sub-array with java.
package com.array2;
public class SplitArray {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = { 1, 2, 3, 5, 4, 6, 9, 8, 15, 52, 4, 6, 89 };
splitArray(a);
}
private static void splitArray(int[] a) {
// TODO Auto-generated method stub
int a_l = a.length;
int[] a1 = new int[a.length / 3];
int[] a2 = new int[a.length / 3];
int[] a3 = new int[a.length / 3 + a.length % 3];
for (int i = 0; i < a3.length; i++) {
if (i < a1.length) {
a1[i] = a[i];
a2[i] = a[a1.length + i];
a3[i] = a[a1.length + a2.length + i];
} else {
a3[i] = a[a1.length + a2.length + i];
}
}
}
}
In the book "elements of programming interviews", I came across, the problem of returning the subarray of the maximum sum. I tried their solution and I don't think we need to keep track of the minimum sum to get the array of the maximum sum:
I wrote another version of it maximumSumMine where I removed the minSum and it worked fine, the output in the comments
What is the purpose of tracking minSum, do we really need it?
#include <stdio.h>
#include <limits.h>
typedef struct range {
int start;
int end;
int maxSum;
} range;
void print(int *a, int start, int end) {
for (int i = start; i <= end; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
// Book's code as it is
range maximumSum(int *a, int n) {
range r;
r.start = 0; r.end = 0;
int minSum = 0, sum = 0, minIndex = -1, maxSum = INT_MIN;
for (int i = 0; i < n; i++) {
sum += a[i];
if (sum < minSum) {
minSum = sum;
minIndex = i;
}
if (sum - minSum > maxSum) {
maxSum = sum - minSum;
r.start = minIndex + 1;
r.end = i + 1;
}
}
return r;
}
range maximumSumMine(int *a, int n) {
range r;
r.start = 0; r.end = 0;
int sum = 0, minIndex = -1, maxSum = INT_MIN;
for (int i = 0; i < n; i++) {
sum += a[i];
if (sum < 0) {
sum = 0;
minIndex = i + 1;
}
if (sum > maxSum) {
maxSum = sum;
r.start = minIndex;
r.end = i;
}
}
return r;
}
void unitTests() {
// Example 1
int a[5] = {-2, 5, 1, -1, 4};
range r = maximumSum(a, 5);
print(a, r.start, r.end); // output 5 1 -1 4 0
// Example 2
int b[5] = {2, -5, 5, -1, 3};
r = maximumSum(b, 5);
print(b, r.start, r.end); // 5 -1 3 1
// Example 1
r = maximumSumMine(a, 5);
print(a, r.start, r.end); // output 5 1 -1 4
// Example 2
r = maximumSum(b, 5);
print(b, r.start, r.end); // 5 -1 3 1
}
int main() {
unitTests();
return 0;
}
You need the minimum sum because the algorithm involves computing prefix sums:
sums[i] = a[0] + a[1] + ... + a[i]
So for each i, the maximum sum you can get that ends at a[i] is sums[i] - min(sums[j < i]).
The book code implements this without actually using an array, as you can simply keep track of the minimum and the current prefix sum.
If you only take the max of the prefix sums under the conditions that you do, it will not work for negative maximum sums: you will always output 0 if the maximum sum is negative, because you will set your prefix sum to 0 when it becomes negative.
Sometimes, ignoring negative maximum sums can be perfectly fine, other times not. I've seen both versions given as programming assignments / questions.
Example:
a = {-1, -2, -3}
book output = -1
your output = 0
I have an array of n elements and for each element I'm trying to find the highest value from the last k values (including the current value)? E.g. given k,
int[] highestValues = new int[arr.Length];
for (int i = k - 1; i < arr.Length; i++)
{
int highest = Int32.MinValue;
for (int j = i - k; j <= i; j++)
{
highest = Math.Max(highest, arr[j]);
}
highestValues[i] = highest;
}
It appears the complexity of this algorithm is O(n^2). I was wondering if there was any way to do it quicker, e.g. rather than comparing k numbers in the inner loop, reuse the result from the previous operation.
There is a nice solution for this problem, using Dequeue data structure (a structure which support add and check element at first and last position).
We create class Entry to store both index of the element and value of that element. Observe that the time complexity is approximately 2*n = O(2*n) = O(n) (Each element is added only once to the queue, and we also iterate through each element in the queue once: remove it either at first or last position of the queue).
Pseudo code:
class Entry {
int value, index;
}
int [] highestValues;
Dequeue queue = new Dequeue();
for(int i = 0; i < arr.Length; i++){
Entry en = new Entry(arr[i], i);
while(queue.last().value <= en.value){//Remove all elements smaller than the current element.
queue.removeLast();
}
queue.append(en);
if(i >= k){
highestValues[i] = queue.first().value;
while(queue.first().index < i - k){//Remove all elements which has index out of the range
queue.removeFirst();
}
}
}
}
This is the maximum sliding window problem or Array of maximum value in sliding window.
See:
http://flexaired.blogspot.gr/2013/05/array-of-maximum-value-in-sliding-window.html
http://tech-queries.blogspot.gr/2011/05/sliding-window-maximum.html
The key to this is the double ended queue that allowed removing elements from beginning and end in order to have maintain what is the maximum as we slide along.
Example from sources:
import java.util.ArrayDeque;
import java.util.Deque;
public class SlidingWindow {
public static void maxSlidingWindow(int A[], int n, int w, int B[]) {
Deque<Integer> Q = new ArrayDeque<Integer>();
// Initialize deque Q for first window
for (int i = 0; i < w; i++) {
while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
Q.pollLast();
Q.offerLast(i);
}
for (int i = w; i < n; i++) {
B[i - w] = A[Q.getFirst()];
// update Q for new window
while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
Q.pollLast();
// Pop older element outside window from Q
while (!Q.isEmpty() && Q.getFirst() <= i - w)
Q.pollFirst();
// Insert current element in Q
Q.offerLast(i);
}
B[n - w] = A[Q.getFirst()];
}
public static void main(String args[]) {
int k = 20;
int a[] = new int[100];
for(int i=0; i<100; i++)
a[i] = i;
int b[] = new int[a.length - k + 1];
maxSlidingWindow(a, a.length, k, b);
System.out.println("Sliding Window Maximum is ");
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + ",");
}
}
}
I have an implementation that I believe works (coded in C#):
public static int[] LastKHighestValues(int[] array, int k)
{
int[] maxes = new int[array.Length];
int indexOfCurrentMax = 0;
int indexOfNextMax = 0;
for (int i = 0; i < array.Length; i++)
{
if (indexOfNextMax <= indexOfCurrentMax ||
array[i] > array[indexOfNextMax])
{
indexOfNextMax = i;
}
if (i - indexOfCurrentMax >= k)
{
indexOfCurrentMax = indexOfNextMax;
}
if (array[i] > array[indexOfCurrentMax])
{
indexOfCurrentMax = i;
}
maxes[i] = array[indexOfCurrentMax];
}
return maxes;
}
The idea is that you keep two "pointers": one to the current max, and one to the next thing to go to after the current max expires. This can be implemented in one pass through the array (so O(n)).
I have a few passing tests, but I'm far from certain I've covered every corner case:
Puzzle.LastKHighestValues(new[] {4, 3, 1}, 1).Should().Equal(new[] {4, 3, 1});
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 3).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 4).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 3, 2, 1 }, 2).Should().Equal(new[] { 3, 3, 2 });
Puzzle.LastKHighestValues(new[] { 7, 1, 4, 1, 1 }, 3).Should().Equal(new[] { 7, 7, 7, 4, 4 });
Puzzle.LastKHighestValues(new[] { 7, 8, 4, 9, 10 }, 2).Should().Equal(new[] { 7, 8, 8, 9, 10 });
Consider a zero-indexed array A of N integers. Indices of this array are integers from 0 to N−1. Take an index K.
Index J is called an ascender of K if A[J] > A[K]. Note that if A[K] is a maximal value in the array A, then K has no ascenders.
Ascender J of K is called the closest ascender of K if abs(K−J) is the smallest possible value (that is, if the distance between J and K is minimal).
Note that K can have at most two closest ascenders: one smaller and one larger than K.
Here is a C++ solution where complexity is O(n).
Note that there are two loops however each iteration the number of element goes by a factor of 1/2 or the search range goes up by a factor of x2.
For example the first iteration take N time, but the second iteration is already N/2.
vector<long> ascender(vector <long> A)
{
long N = A.size();
vector<long> R(N,0);
vector<long> IndexVector(N,0); //This vector contains the index of elements with R=0
vector<long> RangeVector(N,0); //This vector define the loop range for each element
IndexVector[N-1]=N-1;
unsigned long CompxTest = 0;
for (long counter=0;counter<N;counter++)
{
IndexVector[counter] = counter; // we start that all elements needs to be consider
RangeVector[counter] = 1; // we start by looking only and neighbors
}
long Length = N;
long range;
while (Length>1)
{
long index = 0;
cout<<endl<<Length;
long J;
for (long counter=0;counter<Length;counter++)
{
CompxTest++; // Just to test complexity
J = IndexVector[counter]; // Get the index that need to be consider
range = RangeVector[J];
//cout<<" ("<<A[J]<<","<<J<<")";
if (range > N)
{
cout<<endl<<"Mini assert "<<range<<" N "<<N;
break;
}
if (J<(N-range) && A[J+range] > A[J])
{
R[J] = range;
}
if (J<(N-range) && A[J+range] < A[J] && R[J+range]==0)
{
R[J+range] = range;
}
if (J<(N-range) && A[J] == A[J+range] && R[J+range]==0)
{
R[J+range] = - range;
}
if (R[J]==0) // Didn't find ascender for this element - need to consider in next iteration
{
if (R[J+range]>2) //We can increase the range because the current element is smaller
RangeVector[J] += R[J+range]-2;
if (R[J+range]<-2)
RangeVector[J] += -R[J+range]-2;
RangeVector[J]++;
IndexVector[index] = J;
index++;
}
}
Length = index;
}
for (long counter=0;counter<N;counter++)
{
if (R[counter] < 0)
{
unsigned Value = abs(R[counter]);
if (counter+Value<N && A[counter]<A[counter+Value])
R[counter] = Value;
if (counter > Value && R[counter-Value]==0)
R[counter] = 0;
R[counter] = Value + R[counter-Value];
if (counter > Value && Value < R[counter - Value])
{
long PossibleSolution = R[counter - Value] + Value;
if (PossibleSolution <N && A[PossibleSolution]>A[counter])
R[counter] = abs(counter - PossibleSolution);
}
}
}
cout<<endl<<"Complex "<<CompxTest;
return R;
}
//
// C++ using multimap. -- INCOMPLETE
// The multimap MM is effectively the "inverse" of the input array AA
// since it is ordered by pair(value, index), where index refers to the index in
// input array AA, and value is the value in AA at that index.
// Input AA is of course ordered as (index, value).
// So when we read out of MM in value order, (a sorted set of values), each value
// is mapped to the index in the original array AA.
//
int ascender(int AA[], int N, int RR[]) {
multimap<int, int> MM;
// simply place the AA array into the multimap
int i;
for (i = 0; i < N; i++) {
int value = AA[i];
int index = i;
MM.insert(make_pair(value, index));
}
// simply read the multimap in order,
// and set output RR as the distance from one value's
// original index to the next value's original index.
//
// THIS code is incomplete, since it is wrong for duplicate values.
//
multimap<int, int>::iterator pos;
for (pos = MM.begin(); pos != MM.end(); ++pos) {
int value = pos->first;
int index = pos->second;
++pos;//temporarily move ahead to next item
// NEED to FURTHER CONSIDER repeat values in setting RR
RR[index] = (pos)->second - index;
--pos;
}
return 1;
}
1. Sort the array (if not pre-sorted)
2. Subtract every element with its adjacent element and store result in another
array.
Example: 1 3 5 6 8 -----> (after subtraction) 2 2 1 2
3. Find the minimal element in the new array.
4. Device a logic which would relate the minimal element in the new array to the
two elements in the original one.
public class Solution {
final static int MAX_INTEGER = 2147483647;
public static int maximal(int[] A) {
int max = A[0];
int length = A.length;
for (int i = 1; i < length; i++) {
if (A[i] > max) {
max = A[i];
}
}
return max;
}
public static int ascender(int[] a,int length, int k) {
int smallest = MAX_INTEGER;
int index = 0;
if (k<0 || k>length-1) {
return -1;
}
for (int i = 0; i < length; i++) {
// Index J is called an ascender of K if A[J] > A[K].
if(a[i] > a[k]) {
int abs = Math.abs(i-k);
if ( abs < smallest) {
smallest = abs;
index = i;
}
}
}
return index;
}
public static int[] array_closest_ascenders(int[] A) {
int length = A.length;
int[] R = new int[length];
for (int K = 0; K < length; K++) {
// Note that if A[K] is a maximal value in the array A,
// then K has no ascenders.
// if K has no ascenders then R[K] = 0.
if (A[K] == maximal(A)) {
R[K] = 0;
break;
}
// if K has the closest ascender J, then R[K] = abs(K-J);
// that is, R[K] is equal to the distance between J and K
int J = ascender(A, A.length, K);
if (J != -1) {
R[K] = Math.abs(K - J);
}
}
return R;
}
public static void main(String[] args) {
int[] a = { 4, 3, 1, 4, -1, 2, 1, 5, 7 };
/* int[] a = {-589630174, 806785750, -495838474, -648898313,
149290786, -798171892, 584782920, -288181260, -252589640,
133741336, -174886978, -897913872 }; */
int[] R = array_closest_ascenders(a);
for (int element : R) {
System.out.print(element + " ");
}
}
}
Some notes about the code. I guess break in array_closest_ascenders method should be replaced by continue so that all elements are analyzed for their ascenders.
And, surely, maximal(A) have to be moved out of a loop; instead assign maximal value to some variable before entering the loop and use it within the loop, thus avoiding redundant calculation of max value.
Here is C# Solution
class Program
{
static void Main(string[] args)
{
int[] A = new int[] { 4, 3, 1, 4, -1, 2, 1, 5, 7 };
int[] B = new int[A.Length];
int[] R = new int[A.Length];
Program obj = new Program();
obj.ABC(A,B, R);
}
public void ABC(int[] A,int[]B, int[] R)
{
int i, j, m,k;
// int temp = 0;
int n = A.Length - 1;
for (i = 0; i < n; i++)
{
for (j = 0; j <= n; j++)
{
if (A[i] < A[j])
{
m = Math.Abs(j - i);
R[i] = m;
break;
}
}
for (j = i-1; j > 0; j--)
{
if (A[i] < A[j])
{
k = Math.Abs(j - i);
B[i] = k;
break;
}
}
}
for (i = 0; i < n; i++)
{
if (R[i] > B[i] && (B[i] == 0))
{
R[i] = R[i];
//Console.WriteLine(R[i]);
//Console.ReadLine();
}
else { R[i] = B[i]; }
}
}
}
Basically in the search function I compare the first element of the array with the one immediately right, if it's bigger this means it is the first closest ascendant. For the other elements I compare the one immediately at left and afterward the one immediately right his first right element. The first one which is bigger is the closest ascendant, and I keep iterate this way until I don't find an element bigger than one I am considering or I return 0.
class ArrayClosestAscendent {
public int[] solution(int[] A) {
int i;
int r[] = new int[A.length];
for(i=0;i<A.length;i++){
r[i] = search(A, i);
}
return r;
}
public int search(int[] A, int i) {
int j,k;
j=i+1;
k=i-1;
int result = 0;
if(j <= A.length-1 && (A[j]>A[i]))
return Math.abs(j-i);
j++;
while(k>=0 || j < A.length){
if(k >= 0 && A[k] > A[i]){
return Math.abs(i-k);
}else if(j < A.length && A[j] > A[i]){
return Math.abs(i-j);
}else{
j++;
k--;
}
}
return result;
}
}