Let say I have array A and B ( always equal size)
A = 5 4 2 1
B = 8 3 6 7
I am to insert elements from B in to A while keeping the relative order of A while minimising inversions.
So the answer would be 3 5 4 1 2 6 7 8 (7 inversions)
I have tried sorting B first then poping min(a[0] b[0]) into an array C but cases like
A = 99999 1 2 3
B = 5 6 7 8
Gives the wrong 5 6 7 8 99999 1 2 3 (15 inversions)
When the correct is 99999 1 2 3 5 6 7 8 (7 inversions)
I am at a lost please help
Okay, this is an interesting question. My current solution works in O(nlog(n)).
The logic behind this solution can be found here.
However, since there was no implementation there, I've decided to place it here.
The overall logic relies on two things:
For the array that does not need to maintain order, sort it in O(n log n).
Find the middle element, and insert it into the location in the array that minimizes inversions. This can be done in T(2n) = O(n).
The elements to the left of that middle element in the sorted array should be on the left of that element in the combined array to minimize inversions. The elements to the right of that middle element in the sorted array should be on the right of that element in the combined array to minimize inversions. This knowledge reduces the space you have to search to find the insertion point with least inversions each iteration. Hence, you can recurse by applying the above logic on the left half of the sorted array with the left half of the combined array.
Now, onto the code:
#include <bits/stdc++.h>
using namespace std;
long long merge(vector<int>& v, int temp[], int left, int mid, int right) {
int i,j,k;
long long inv_count = 0;
i = left;
j = mid;
k = left;
while ((i <= mid - 1) && (j <= right)) {
if (v[i] <= v[j]) {
temp[k++] = v[i++];
} else {
temp[k++] = v[j++];
inv_count += mid - i;
}
}
while (i <= mid - 1)
temp[k++] = v[i++];
while (j <= right)
temp[k++] = v[j++];
for (i = left; i <= right; i++)
v[i] = temp[i];
return inv_count;
}
long long _mergeSort(vector<int>& v, int temp[], int left, int right) {
int mid;
long long inv_count = 0;
if (v.size() < 2) return 0;
if (right > left) {
mid = (right + left) / 2;
inv_count += _mergeSort(v, temp, left, mid);
inv_count += _mergeSort(v, temp, mid + 1, right);
inv_count += merge(v, temp, left, mid + 1, right);
}
return inv_count;
}
long long countInversionsInVector(vector<int>& v) {
int temp[v.size()];
return _mergeSort(v, temp, 0, v.size() - 1);
}
void solveHelper(vector<int> &a, int sa, int ea, vector<int>& b, int sb, int eb, vector<int>& I) {
if (sb >= eb) return;
int mb = (sb + eb) / 2;
int b_elem = b[mb];
vector<int> sliced_a(ea - sa + 1);
for (auto index = a.begin() + sa; index < a.begin() + ea; index++) {
sliced_a.push_back(*index);
}
long long invCount = 0;
for (long long i = sa; i < ea; i++) {
if (a[i] < b_elem) {
invCount += 1;
}
}
long long minInvCount = invCount;
int insertionIndex = sa;
for (int i = sa; i < ea; i++) {
if (a[i] < b[mb]){
invCount -= 1;
} else {
invCount += 1;
}
if (invCount < minInvCount) {
minInvCount = invCount;
insertionIndex = i + 1;
}
}
I[mb] = insertionIndex;
solveHelper(a, sa, insertionIndex, b, sb, mb, I);
solveHelper(a, insertionIndex, ea, b, mb + 1, eb, I);
return;
}
void mergeFinal(vector<int>& a, vector<int>& b, vector<int>& I, vector<int>& final) {
int index_a = 0;
int index_b = 0;
for (int i = 0; i < 2 * a.size(); i++) {
if (i == I[index_b] + index_b) {
final[i] = b[index_b];
index_b++;
} else {
final[i] = a[index_a];
index_a++;
}
}
}
long long solve(vector<int>& a, vector<int>& b) {
sort(b.begin(), b.end());
vector<int> I(b.size());
solveHelper(a, 0, a.size(), b, 0, b.size(), I);
vector<int> final(2*a.size());
mergeFinal(a, b, I, final);
return countInversionsInVector(final);
}
int main() {
int n;
ios_base::sync_with_stdio(false); // speed up reading input
cin >> n;
vector<int> a(n), b(n);
for(int i = 0; i < n; i++) {
cin >> a[i];
}
for(int i = 0; i < n; i++) {
cin >> b[i];
}
cout << solve(a, b) << "\n";
}
Related
I hope i made my self clear enough in the title but if not i am here to explain my self
i got an array from an input ( like Arr = {, ).
we can use only 1 additional array (1 original 1 additional)
this is what i made so far :
I made a new array named newArr and assigned it all the values Arr contains.
i sorted it (because its requires time complexity of nlogn)
and then moved duplicates to the end.
now what i can't figure out :
now i need to move the original digits to their place according to the main
(all the values in the arrays are positive and they can be bigger then
n-which is the size of the array and ofc they can be also smaller then n)
i also need to return the number of original digits in the array
the original number should stay in the same position and the duplicates in the end of the array their order doesn't matter.
from here we can't use another additional array only the current arrays that we have ( which are 2)
i have been thinking about doing some kind of binary search but all of them went wrong.(like bin_search_first) and original binary and still couldn't manage it.
can some one give me an hint?
here is the code at where i am
#define _CRT_SECURE_NO_WARNINGS
/*Libraries*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
int* input_array(int);
int moveDuplicatesV2(int*, int);
void merge(int* a, int p, int q, int r);
void merge_sort(int* a, int first, int last);
void swap(int* v, int* u);
int bin_search_first(int , int* , int );
int main()
{
int arr[10] = { };
int n = 12;
int k = 0;
int first = 0;
int last = n - 1;
int mid = (first + last) / 2;
int l = n - 1;
int* D = arr + 1;
int j = 0;
size_t dupes_found = 0;
int* newArr = (int*)malloc(12 * sizeof(int));
assert(newArr);
for (int i = 0; i < n; i++)
{
newArr[i] = arr[i];
}
merge_sort(newArr, first, last);
for (size_t i = 0; i < n - 1 - dupes_found;)
{
if (newArr[i] == newArr[i + 1])
{
dupes_found++;
int temp = newArr[i];
memmove(&newArr[i], &newArr[i + 1], sizeof(int) * (n - i - 1));
newArr[n - 1] = temp;
}
else {
i++;
}
}
j = 0;
int key = 0;
first = 0;
for (int i = 0; i < n - dupes_found; i++)
{
key = newArr[i];
first = bin_search_first(key, arr,n);
swap(&newArr[i], &newArr[first]);
newArr[first] = newArr[i];
}
for (int i = 0; i < n; i++)
{
arr[i] = newArr[i];
}
for (int i = 0; i < n; i++)
{
printf("%d", arr[i]);
}
return n - dupes_found;
}
void merge(int* a, int p, int q, int r)
{
int i = p, j = q + 1, k = 0;
int* temp = (int*)malloc((r - p + 1) * sizeof(int));
assert(temp);
while ((i <= q) && (j <= r))
if (a[i] < a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
while (j <= r)
temp[k++] = a[j++];
while (i <= q)
temp[k++] = a[i++];
/* copy temp[] to a[] */
for (i = p, k = 0; i <= r; i++, k++)
a[i] = temp[k];
free(temp);
}
void merge_sort(int* a, int first, int last)
{
int middle;
if (first < last) {
middle = (first + last) / 2;
merge_sort(a, first, middle);
merge_sort(a, middle + 1, last);
merge(a, first, middle, last);
}
}
void swap(int* v, int* u)
{
int temp;
temp = *v;
*v = *u;
*u = temp;
}
int bin_search_first(int key, int* a, int n)
{
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high)
{
mid = (low + high) / 2; // low + (high - low) / 2
if (key > a[mid])
low = mid + 1;
else
if (key < a[mid])
high = mid - 1;
else //key==a[mid]
if ((low == high) || (a[mid - 1] < key))
return mid;
else
high = mid - 1;
}
return -1;
}
Here is my idea:
Sort the array (nlogn)
Loop over the array and for each value, save a pointer to its first occurence (n)
Loop over the original array and insert the value into a result array if it is the values first occurrence. Whether or not it is the first occurrence can be checked using the sorted array: each element in this array has an additional flag that will be set if the value has already been seen. So, search for the element using bsearch, if seen append to back of result array (order does not matter), if not seen append to beginning of array and set seen value. (nlogn, since bsearch doesn't need to seek the first element because it was precomputed thus logn, over the array n)
Here is an example code (you can replace the qsort by mergesort to make the algorithm actually nlogn, I just used qsort because it is given):
#include <stdio.h>
#include <stdlib.h>
struct arr_value {
int value;
int seen;
struct arr_value *first;
};
int compar(const void *p1,const void *p2) {
struct arr_value *v1 = (struct arr_value *)p1;
struct arr_value *v2 = (struct arr_value *)p2;
if(v1->value < v2->value) {
return -1;
} else if(v1->value == v2->value) {
return 0;
}
return 1;
}
int main()
{
#define NumCount (12)
int arr[NumCount] = { 7, 3, 1, 2, 7, 9, 3, 2, 5, 9, 6, 2 };
int arrResult[NumCount];
int resultCount = 0;
int resultCountBack = 0;
struct arr_value arrseen[NumCount];
for(int i = 0; i < NumCount; ++i) {
arrseen[i].value = arr[i];
arrseen[i].seen = 0;
}
qsort(arrseen, NumCount, sizeof(struct arr_value), compar);
struct arr_value *firstSame = arrseen;
firstSame->first = firstSame;
for(int i = 1; i < NumCount; ++i) {
if(arrseen[i].value != firstSame->value) {
firstSame = arrseen + i;
}
arrseen[i].first = firstSame;
}
struct arr_value key;
for(int i = 0; i < NumCount; ++i) {
key.value = arr[i];
struct arr_value *found = (struct arr_value *)bsearch(&key, arrseen, NumCount, sizeof(struct arr_value), compar);
struct arr_value *first = found->first;
if(first->seen) {
// value already seen, append to back
arrResult[NumCount - 1 - resultCountBack] = first->value;
++resultCountBack;
} else {
// value is new, append
arrResult[resultCount++] = first->value;
first->seen = 1;
}
}
for(int i = 0; i < NumCount; ++i) {
printf("%d ", arrResult[i]);
}
return 0;
}
Output:
7 3 1 2 9 5 6 2 9 2 3 7
To begin with, memmove doesn't run in a constant time, so the loop
for (size_t i = 0; i < n - 1 - dupes_found;)
{
if (newArr[i] == newArr[i + 1])
{
dupes_found++;
int temp = newArr[i];
memmove(&newArr[i], &newArr[i + 1], sizeof(int) * (n - i - 1));
newArr[n - 1] = temp;
}
else {
i++;
}
}
drives the time complexity quadratic. You have to rethink the approach.
It seems that you are not using a crucial point:
all the values in the arrays are positive
It seriously hints that changing values to their negatives is a way to go.
Specifically, as you iterate over the initial array, and bin-search its elements in temp, comparing the _ absolute values_. When an element is found in temp, and it is still positive there, flip all its dupes in temp to negative. Otherwise flip it in initial.
So far, it is O(n log n).
Then perform an algorithm known as stable_partition: all positives are moved in front of negatives, retaining the order. I must not spell it here - I don't want to deprive you of a joy figuring it out yourself (still O(n log n)
And finally flip all negatives back to positives.
This is the task I have got:
I need to write a function (not recursive) which has two parameters.
An array of integers.
An integer representing the size of the array.
The function will move the duplicates to an end of the array.
And will give the size of the different digits.
Example:
5 , 2 , 4 , 5 , 6 , 7 , 2, n = 7
we will get back 5 , 2 , 4 , 6 , 7 , 5 , 2 and 5
We must keep the original sort as it is (which means like in example 5 must)
It does not matter how we sort the duplicates ones but just keep the sort for the original array as it is)
The function has to print the number of different digits (like in example 5)
The the input range of numbers in array [-n,n]
I can only use 1 additional array for help.
It has to be O(n)
I tried it so many times and feel like am missing something. Would appreciate any advice/suggestions.
int moveDup(int* arr, int n)
{
int* C = (int*)calloc(n * 2 + 1, sizeof(int));
assert(C);
/*int* count = C + n;*/
int *D = arr[0];
int value = 0, count = 0;
for (int i = 0; i < n; i++)
{
value = arr[i];
if (C[value + n] == 0)
{
*D = arr[i];
D++;
count++;
}
C[value + n] = C[value + n] + 1;
}
while (1 < C[value + n])
{
*D = i;
D++;
C[value + n]--;
}
free(C);
return count;
}
This algorithm will produce the required results in O(n) arithmetic complexity:
Input is an array A with n elements indexed from A0 to An−1 inclusive. For each Ai, −n ≤ Ai ≤ n.
Create an array C that can be indexed from C−n to C+n, inclusive. Initialize C to all zeros.
Define a pointer D. Initialize D to point to A0.
For 0 ≤ i < n:
If CAi=0, copy Ai to where D points and advance D one element.
Increment CAi.
Set r to the number of elements D has been advanced from A0.
For −n ≤ i ≤ +n:
While 1 < CAi:
Copy i to where D points and advance D one element.
Decrement CAi.
Release C.
Return r. A contains the required values.
A sample implementation is:
#include <stdio.h>
#include <stdlib.h>
#define NumberOf(a) (sizeof (a) / sizeof *(a))
int moveDuplicates(int Array[], int n)
{
int *memory = calloc(2*n+1, sizeof *Array);
if (!memory)
{
fprintf(stderr, "Error, unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
int *count = memory + n;
int *destination = Array;
for (int i = 0; i < n; ++i)
// Count each element. If it is unique, move it to the front.
if (!count[Array[i]]++)
*destination++ = Array[i];
// Record how many unique elements were found.
int result = destination - Array;
// Append duplicates to back.
for (int i = -n; i <= n; ++i)
while (0 < --count[i])
*destination++ = i;
free(memory);
return result;
}
int main(void)
{
int Array[] = { 5, 2, 4, 5, 6, 7, 2 };
printf("There are %d different numbers.\n",
moveDuplicates(Array, NumberOf(Array)));
for (int i = 0; i < NumberOf(Array); ++i)
printf(" %d", Array[i]);
printf("\n");
}
here is the right answer, figured it out by myself.
int moveDup(int* arr, int n)
{
int* seen_before = (int*)calloc(n * 2 + 1, sizeof(int));
assert(seen_before);
int val = 0, count = 0, flag = 1;
int j = 0;
for (int i = 0; i < n; i++)
{
val = arr[i];
if (seen_before[arr[i] + n] == 0)
{
seen_before[arr[i] + n]++;
count++;
continue;
}
else if (flag)
{
j = i + 1;
flag = 0;
}
while (j < n)
{
if (seen_before[arr[j] + n] == 0)
{
count++;
seen_before[arr[j] + n]++;
swap(&arr[i], &arr[j]);
j++;
if (j == n)
{
free(seen_before);
return count;
}
break;
}
/*break;*/
j++;
if (j == n)
{
free(seen_before);
return count;
}
}
}
}
second right answer
int* mem = (int*)calloc(2 * n + 1, sizeof * arr);
assert(mem);
int* count = mem + n;
int* dest = arr;
for (i = 0; i < n; ++i)
{
if (count[arr[i]]++ == 0)
{
*dest = arr[i];
*dest++;
}
}
res = dest - arr;
for (i = -n; i <= n; ++i)
{
while (0 < --count[i])
{
*dest++ = i;
}
}
free(mem);
return res;
I am implementing mergesort referencing the geeks for geeks implementation as a guide but my implementation is not working.
I have my mergesort function that divides my given array in 2 and calls mergesort on half of the list.
Within mergesort, I use a helper function that merges the sub arrays together.
I have included my 2 functions. Could someone be my second set of eyes, I am have staring at this too long to tell the difference between 1's and l's
It is running but not sorting correctly.
void merge(int arr[], int temp[], int l, int m, int r) {
//TODO: implement merge.
// check arr
if (arr == NULL) {
return;
}
int left = m - l + 1;
int right = r - m;
// copy array into temp array
// first half
int i = 0;
for (i = 0; i < left; i++) {
temp[i] = arr[l + i];
}
// second half
int j = 0;
for (j = m + 1; i < right; j++) {
temp[j] = arr[m + l + i];
}
// compare from each end inserting the lower into the next location of the real array
// beginning index of front sub list
int front = 0;
// beginning of back sub list
int back = left;
// index within array to insert back in
int index = l;
while ((front < left) && (back < right)) {
if (temp[front] <= temp[back]) {
// temp front goes in the next array spot
arr[index] = temp[front];
// increase temp
front++;
} else {
// back is smaller and is put back in the list first
arr[index] = temp[back];
// increase back
back++;
}
// increase array index
index++;
}
while (front < left) {
arr[index] = temp[front];
front++;
index++;
}
while (back < right) {
arr[index] = temp[back];
back++;
index++;
}
}
void mergeSort(int array[], int temp[], int l, int r) {
if (r > l) {
// find middle point
int middle = l + (r - l) / 2;
// call merge on first half
mergeSort(array, temp, l, middle);
// call merge on second half
mergeSort(array, temp, middle + 1, r);
// merge the halves
merge(array, temp, l, middle, r);
}
}
There is a problem here:
// second half
int j = 0;
for (j = m + 1; i < right; j++) {
temp[j] = arr[m + l + i];
}
You should write:
// second half
for (i = 0; i < right; i++) {
temp[left + i] = arr[m + 1 + i];
}
Could someone be my second set of eyes, I am have staring at this too long to tell the difference between 1's and l's?
This is an excellent point! The solution is to never use l as a variable name, and to simplify the mergesort implementation to remove the confusing and error prone +1 / -1 adjustments. For this you just need to use the convention where r is the index of the element after the end of the slice.
Here is a modified version:
void merge(int arr[], int temp[], int lo, int mid, int hi) {
// check arr
if (arr == NULL) {
return;
}
int left = mid - lo;
int right = hi - mid;
// copy array into temp array
// first half
for (int i = 0; i < left; i++) {
temp[i] = arr[lo + i];
}
// second half
for (int i = 0; i < right; i++) {
temp[left + i] = arr[mid + i];
}
// compare from each end inserting the lower into the next location of the real array
// beginning index of front sub list
int front = 0;
// beginning of back sub list
int back = left;
// index within array to insert back in
int index = lo;
while ((front < left) && (back < right)) {
if (temp[front] <= temp[back]) {
// temp front goes in the next array spot
arr[index++] = temp[front++];
} else {
// back is smaller and is put back in the list first
arr[index++] = temp[back++];
}
}
while (front < left) {
arr[index++] = temp[front++];
}
while (back < right) {
arr[index++] = temp[back++];
}
}
void mergeSort(int array[], int temp[], int lo, int hi) {
if (hi - lo >= 2) {
// find middle point
int mid = lo + (hi - lo) / 2;
// call merge on first half
mergeSort(array, temp, lo, mid);
// call merge on second half
mergeSort(array, temp, mid, hi);
// merge the halves
merge(array, temp, lo, mid, hi);
}
}
The initial call should give 0 and the length of the array as index arguments.
I had some trouble understanding your merge logic.
Too many indexes/limits of the form (e.g): m - l + 1
So, I simplified things by having two temp pointers: tmp_l and tmp_r and added some length/count variables.
This allowed some of the indexes to start with 0.
Also, your middle calculation was unusual.
And, I changed the arg in the call to merge from middle to middle + 1
I've refactored the code and added a test suite. I've used cpp conditionals to denote old vs new code:
#if 0
// old code
#else
// new code
#endif
Anyway, here's my [working] version:
#include <stdio.h>
#include <stdlib.h>
#ifdef DEBUG
#define dbgprt(_fmt...) \
do { \
printf(_fmt); \
} while (0)
#else
#define dbgprt(_fmt...) \
do { \
} while (0)
#endif
void
merge(int arr[], int temp[], int l, int m, int r)
{
if (arr == NULL) {
return;
}
// get number of left elements
int lcnt = (m - l);
// get number of right elements
int rcnt = (r - m) + 1;
dbgprt("merge: BEGIN l=%d m=%d r=%d lcnt=%d rcnt=%d\n",
l,m,r,lcnt,rcnt);
int i = 0;
int *tmp_l = &temp[0];
for (i = 0; i < lcnt; i++)
tmp_l[i] = arr[l + i];
int *tmp_r = &temp[lcnt];
for (i = 0; i < rcnt; i++)
tmp_r[i] = arr[m + i];
int front = 0;
int back = 0;
int index = l;
while ((front < lcnt) && (back < rcnt)) {
// temp front goes in the next array spot
if (tmp_l[front] <= tmp_r[back]) {
arr[index] = tmp_l[front];
// increase temp
front++;
}
// back is smaller and is put back in the list first
else {
arr[index] = tmp_r[back];
// increase back
back++;
}
// increase array index
index++;
}
while (front < lcnt) {
arr[index] = tmp_l[front];
index++;
front++;
}
while (back < rcnt) {
arr[index] = tmp_r[back];
index++;
back++;
}
}
void
mergeSort(int array[], int temp[], int l, int r)
{
int middle;
if (r > l) {
// find middle point
#if 0
middle = l + (r - 1) / 2;
#else
middle = (l + r) / 2;
#endif
dbgprt("msort: ENTER l=%d m=%d r=%d\n",l,middle,r);
// call merge on first half
mergeSort(array, temp, l, middle);
// call merge on second half
mergeSort(array, temp, middle + 1, r);
// merge the halves
#if 0
merge(array, temp, l, middle, r);
#else
merge(array, temp, l, middle + 1, r);
#endif
dbgprt("msort: EXIT l=%d m=%d r=%d\n",l,middle,r);
}
}
void
dotest(int tstno)
{
int count = (rand() % 30) + 1;
printf("dotest: %d %d\n",tstno,count);
int *arr = malloc(sizeof(*arr) * count);
int *tmp = malloc(sizeof(*tmp) * count);
for (int idx = 0; idx < count; ++idx) {
arr[idx] = count - idx;
dbgprt(" %d",arr[idx]);
}
dbgprt("\n");
mergeSort(arr,tmp,0,count - 1);
int err = 0;
for (int idx = 0; idx < count; ++idx) {
printf(" %d",arr[idx]);
if (arr[idx] != (idx + 1)) {
printf("?");
err = 1;
}
}
printf("\n");
if (err)
exit(1);
free(arr);
free(tmp);
}
int
main(void)
{
for (int tstno = 1; tstno <= 4; ++tstno)
dotest(tstno);
return 0;
}
void
merge_ORIG(int arr[], int temp[], int l, int m, int r)
{
if (arr == NULL) {
return;
}
int i = 0;
for (i = 0; i < m - l + 1; i++) {
temp[i] = arr[l + i];
}
for (i = 0; i < r + 1; i++) {
temp[i] = arr[m + l + i];
}
int front = l;
int back = m + 1;
int index = l;
while ((front < m - l + 1) && (back < r - m)) {
if (temp[front] <= temp[back]) {
// temp front goes in the next array spot
arr[index] = temp[front];
// increase temp
front++;
}
else {
// back is smaller and is put back in the list first
arr[index] = temp[back];
// increase back
back++;
}
// increase array index
index++;
}
while (front < m - l + 1) {
arr[index] = temp[front];
index++;
front++;
}
while (back < r - m) {
arr[index] = temp[back];
index++;
back++;
}
}
Given a very large integer array, I need to find the maximum value of a4, such that:
a4 = a1 + a2 + a3
Where the ai's are all values in the array.
How would I do this?
Note: Using 4 for loops is not the ideal solution.
There is a simple (expected) O(n^2) solution:
Iterate through all pairs of array elements (a, b) and store their sum in a hash table.
Iterate through all candidate pairs (a4, a1) and check whether a4 - a1 is in the table. The maximum over all valid a4 is the solution. Of course you should process a4 from largest to smallest, but that doesn't affect the asymptotics.
If you want to avoid using an element more than once, you need some additional information stored in the hash table so that you can filter out pairs that colide with a1 or a4 fast.
If the integers in the array are bounded (max - min <= C), it might be useful to know that you can achieve O(n + C log C) using a discrete fourier transform (solvable using FFT).
First of all you should ascending sort your array. then start from the last (biggest) member of the array.
For example, for [1,2,3,777,999,111,665] you'll have sortedArray = {1,2,3,111,665, 777, 999}
then select 999 as a4 and try to create it with other members. So you should select as a3 and try to create (999 - 777) = 222 as a1+a2 since your array is sorted you only need to consider subarray {1,2,3,111}. if there is no pair satisfying this condition, try next biggest member (777) and retry above scenario to find the solution
Based on #Niklas answer, I wrote the following program in Java.
public static int sumOfThree(int [] arr) {
int arrlen = arr.length;
int arrijv [][] = new int [arrlen * (arrlen - 1) / 2][3];
int arrijvlen = 0;
quickSortInDescendingOrder(arr, 0, arrlen - 1); // sorts array into descending order
System.out.println(Arrays.toString(arr));
// populates array with sum of all pair values
for (int i = 0; i < arrlen - 1; i++) {
for (int j = i + 1; j < arrlen; j++) {
// if ((arr[i] + arr[j]) < arr[0]) { // can be added if no negative values
arrijv[arrijvlen][0] = i;
arrijv[arrijvlen][1] = j;
arrijv[arrijvlen][2] = arr[i] + arr[j];
arrijvlen++;
// }
}
}
System.out.print('[');
for (int i = 0; i < arrijvlen; i++) {
System.out.print(arrijv[i][2] + " ");
}
System.out.print("]\n");
// checks for a match of difference of other pair in the populated array
for (int i = 0; i < arrlen - 1; i++) {
for (int j = i + 1; j < arrlen; j++) {
int couldBeA4 = arr[i];
if(isAvailable(arrijv, arrijvlen, couldBeA4 - arr[j], i, j)){
System.out.println(" i3-" + j + " i4-" + i);
return couldBeA4;
}
}
}
return -1;
}
private static boolean isAvailable(int[][] arrijv, int len, int diff, int i, int j) {
boolean output = false;
// returns true if the difference is matched with other combination of i,j
for (int k = 0; k < len; k++) {
if (arrijv[k][2] == diff) {
int pi = arrijv[k][0];
int pj = arrijv[k][1];
if (pi != i && pi != j && pj != i && pj != j) {
System.out.print("i1-" + pj + " i2-" + pi);
output = true;
break;
}
}
}
return output;
}
private static void quickSortInDescendingOrder(int[] array, int low, int high) { // solely used for sorting input array into descending array
if (low < high) {
int partition = getPartitionIndex(array, low, high);
quickSortInDescendingOrder(array, low, partition);
quickSortInDescendingOrder(array, partition + 1, high);
}
}
private static int getPartitionIndex(int[] arr, int lo, int hi) {
int pivot = arr[(lo + hi) / 2];
while (true) {
while (arr[lo] > pivot) {
lo++;
}
while (arr[hi] < pivot) {
hi--;
}
if (arr[lo] == arr[hi]) { // can be removed if no duplicate values
return lo;
} else if (lo < hi) {
int temp = arr[lo];
arr[lo] = arr[hi];
arr[hi] = temp;
} else {
return hi;
}
}
}
Please verify that it works and suggest for further improvements.
I am writing a code to find the maximum sum contiguous sub array in C. The logic seems fine according to me, but still the output is not correct. Please look into the code. The algorithm divides a bigger array into 2 sub-arrays. It then checks for maximum sum sub-array by examining the left array , right array and also the array containing the midpoint (It will check right and left from the midpoint and then return the maximum sum sub-array containing the midpoint).
int* cross_max(int arr[], int low, int mid, int high)
{
int left_max, left_sum = -2000;
int sum = 0;
int i;
for(i=mid; i>=low;i--)
{
sum = sum + arr[i];
if(sum > left_sum)
{
left_sum = sum;
left_max = i;
}
}
int right_max, right_sum = -2000;
for(i=mid+1; i<=high;i++)
{
sum = sum + arr[i];
if(sum > right_sum)
{
right_sum = sum;
right_max = i;
}
}
// 0 - sum
// indices - 1,2
int temp_arr[3] = {0,0,0};
temp_arr[0] = left_sum + right_sum;
temp_arr[1] = left_max;
temp_arr[2] = right_max;
int *p = temp_arr;
printf("\n Maximum sum = %d\n",*p);
printf("\n low = %d\n",*(p+1));
printf("\n high = %d\n",*(p+2));
return p;
}
int* find_max(int arr[], int low, int high)
{
int temp_arr[3] = {0,0,0};
if(low == high)
{
temp_arr[0] = arr[low];
temp_arr[1] = low;
temp_arr[2] = low;
int *q = temp_arr;
return q;
}
int mid = (low + high)/2;
int* a1 = find_max(arr,low,mid);
int* a2 = find_max(arr,mid+1,high);
int* a3 = cross_max(arr,low,mid,high);
if (*a1 > *a2 && *a1 > *a3)
return a1;
else if (*a2 > *a1 && *a2 > *a3)
return a2;
else
return a3;
}
int main()
{
int arr[8] = {1,1,2,-2,3,3,4,-4};
int *point = find_max(arr,0,7);
printf("\n Maximum sum = %d\n",*point);
printf("\n low = %d\n",*(point+1));
printf("\n high = %d\n",*(point+2));
return 0;
}
Slightly off-topic, but this problem is well-known for the best way to solve it (in linear time). You can completely derive the code from the specification.
First, define the problem formally:
Given: integer array A[0, N)
Required:
max(0 <= p <= q <= N : sum(p, q))
where sum(p, q) = sum(p <= i < q : A[i])
Approach:
Let X(n) = max(0 <= p <= q <= n : sum(p, q)), then we need to find X(N). We do this by induction:
X(0) = max(0 <= p <= q <= 0 : sum(p, q))
= sum(0, 0)
= sum(0 <= i < 0 : A[i])
= 0
and
X(n+1) = max(0 <= p <= q <= n+1 : sum(p, q))
= max(max(0 <= p <= q <= n : sum(p, q)), max(0 <= p <= n+1 : sum(p, n+1)))
= max(X(n), Y(n+1))
where Y(n) = max(0 <= p <= n : sum(p, n)). We now also determine Y(n) by induction:
Y(0) = max(0 <= p <= 0 : sum(p, 0))
= sum(0, 0)
= 0
and
Y(n+1) = max(0 <= p <= n+1 : sum(p, n+1))
= max(max(0 <= p <= n : sum(p, n+1)), sum(n+1, n+1)))
= max(max(0 <= p <= n : sum(p, n)) + A[n], 0)
= max(Y(n) + A[n], 0)
Code:
Using the analysis above, the code is trivial.
int arr[8] = {1,1,2,-2,3,3,4,-4};
int N = 8;
int x = 0;
int y = 0;
for (int n = 0; n < N; n++) {
y = max(y + arr[n], 0);
x = max(x, y);
}
printf("Maximum sum = %d\n", x);
with
int max(int a, int b) {
if (a > b)
return a;
else
return b;
}
There are a couple of problems with undefined behavior in your code:
The first is that you pass 9 as high which will be used to index the tenth element of an eight-element array. It will be the tenth because in cross_max you loop while i <= high, so you will index arr[9]. Remember that array indexes are from zero to the size minus one (so you can index from 0 to 7 for your array). The indexes out of bounds will contain undefined (i.e. random) values.
The second problem is that you are returning pointers to a local variable from cross_max. This will lead to undefined behavior when you use that returned pointer. Local variables are only valid inside the scope they were declared, and when the function returns the memory area used by the local variables will be reclaimed and used for the next function.
this is helper to get the max value.
int maxcmp(int a, int b) {
return a >= b ? a : b;
}
The idea is as you iterate over the nums, you add them together. If your cur_sum is less than 0 up to that point, you eliminate all the numbers so far. Because adding negative value after that point is not going to increase the total sum for the rest of nums.
int maxSubArray(int* nums, int numsSize){
int maxSoFar = nums[0],
cur_sum = 0;
for(int i = 0; i < numsSize; i++) {
if (cur_sum<0){
cur_sum=0;
}
cur_sum=cur_sum+nums[i];
maxSoFar=maxcmp(maxSoFar,cur_sum);
}
return maxSoFar;
}`enter code here`
The algorithm is not very efficient. The time complexity is o(n^2). Here is a dynamic programming algorithm, which is o(n).
/*************************************************************************
> File Name: subarray.cpp
> Author: luliang
> Mail: lulyon#126.com
> Created Time: 2013/09/10 Tuesday 15:49:23
************************************************************************/
#include <stdio.h>
typedef struct {
int low;
int high;
int sum;
}DPInfoType;
int main()
{
int arr[8] = {1,1,2,-2,3,3,4,-4};
const int n = sizeof(arr) / sizeof(arr[0]);
DPInfoType dp[n];
dp[0].low = 0;
dp[0].high = 0;
dp[0].sum = arr[0];
for(int i = 1; i < n; ++i) {
if(dp[i - 1].sum > 0) {
dp[i].low = dp[i - 1].low;
dp[i].high = i;
dp[i].sum = dp[i - 1].sum + arr[i];
}
else {
dp[i].low = i;
dp[i].high = i;
dp[i].sum = arr[i];
}
}
int max_index = 0;
for(int i = 1; i < n; ++i) {
if(dp[max_index].sum < dp[i].sum) max_index = i;
}
printf("\n Maximum sum = %d\n", dp[max_index].sum);
printf("\n low = %d\n", dp[max_index].low);
printf("\n high = %d\n", dp[max_index].high);
return 0;
}
As already mentioned use of pointers is inappropriate in your code.
This code worked for me.
#include <stdio.h>
#define INF 1000000
int max (int a, int b)
{
if (a < b)
return b;
return a;
}
int findMaxCrossingSubarray (int arr[], int low, int mid, int high, int *start, int *end)
{
int i, left, right;
int max_left, max_right;
int left_sum = -INF;
int sum = 0;
for (i = mid; i >= 0; i--) {
sum += arr[i];
if (sum > left_sum) {
left_sum = sum;
max_left = i;
}
}
int right_sum = -INF;
sum = 0;
for (i = mid + 1; i <= high; i++) {
sum += arr[i];
if (sum > right_sum) {
right_sum = sum;
max_right = i;
}
}
*start = max_left;
*end = max_right;
return left_sum + right_sum;
}
int findMaxSubarray (int arr[], int low, int high, int *start, int *end)
{
if (low == high)
return arr[low];
int mid = (high - low)/2 + low;
int start1, start2, start3;
int end1, end2, end3;
// initialization of start and end for terminal cases.
start1 = start3 = low;
start2 = mid + 1;
end1 = mid;
end2 = end3 = high;
int sum1 = findMaxSubarray(arr, low, mid, &start1, &end1);
int sum2 = findMaxSubarray(arr, mid + 1, high, &start2, &end2);
int sum3 = findMaxCrossingSubarray(arr, low, mid, high, &start3, &end3);
int res = max(max(sum1, sum2), sum3);
if (res == sum1) {
*start = start1;
*end = end1;
}
if (res == sum2) {
*start = start2;
*end = end2;
}
if (res == sum3) {
*start = start3;
*end = end3;
}
return res;
}
int main(int argc, char const *argv[])
{
int size, i, item, result;
printf("Enter the size of array: ");
scanf("%d",&size);
int arr[size];
printf("Enter the array:\n");
for (i = 0; i < size; ++i) {
scanf("%d",&item);
arr[i] = item;
}
int start = 0, end = size-1;
result = findMaxSubarray(arr, 0, size-1, &start, &end);
printf("Result: %d, start: %d and end: %d.\n", result, start, end);
return 0;
}