Can I partition the array in K sizes? - algorithm

I am trying to implement the algorithm from this question: Need idea for solving this algorithm puzzle, but i am missing some edge case which is causing my code to go in infinite loop. I can fix it by doing some cosmetic change but it shows that i didn't understand the algorithm.
Can someone help me out, what i am missing?
#include <stdio.h>
#define max(a, b) (((a)>(b))?(a):(b));
int get_max(int *a, int i, int size)
{
if (i >= size)
return 0;
return max(a[i], get_max(a, i+1, size));
}
int get_sum(int *a, int i, int size)
{
if (i >= size)
return 0;
return a[i] + get_sum(a, i+1, size);
}
int get_partition(int *a, int size, int bound) {
int running_sum = 0;
int partitions = 0, i;
for (i=0;i<size;i++) {
if (a[i] + running_sum <= bound) {
running_sum += a[i];
} else {
running_sum = 0;
running_sum += a[i];
partitions++;
}
}
return partitions;
}
int foo(int *a, int size, int k)
{
int lower = get_max(a, 0, size);
int higher = get_sum(a, 0, size);
int partition;
while (lower < higher) {
int bound = (lower + (higher))/2;
partition = get_partition(a, size, bound);
printf("partition %d bound %d lower %d higher %d\n", partition, bound, lower, higher);
if (partition >= k)
lower = bound;
else
higher = bound;
}
return partition;
}
#define SIZE(a) sizeof(a)/sizeof(a[0])
int main(void) {
int a[] = {2, 3, 4, 5, 6};
printf("%d\n", foo(a, SIZE(a), 3));
return 0;
}
Output:
partition 1 bound 13 lower 6 higher 20
partition 2 bound 9 lower 6 higher 13
partition 3 bound 7 lower 6 higher 9
partition 3 bound 8 lower 7 higher 9
partition 3 bound 8 lower 8 higher 9
...last line keeps repeating.

You have couple of mistakes:
during the binary search, your while test should be while (lower+1 < higher) { and not while (lower < higher) {. You are entering infinite loop when lower = 8, higher = 9. At this stage, your bound would be (lower+higher)/2=8and you would update lower = bound which would not change anything.
at the end of foo you should return higher (not partitions) since your binary search invariant is that for having bound <= lower you can partition the array in more than k parts and forbound >= higher you can partition it in k or less.
your calculation of get_partition is wrong. You don't take into the account the last partition group since you only update partitions when you overflow running_sum. After the for-cycle you should have the statement :
if (running_sum > 0)
partitions++;
Putting it all together:
#include <stdio.h>
#define max(a, b) (((a)>(b))?(a):(b));
int get_max(int *a, int i, int size)
{
if (i >= size)
return 0;
return max(a[i], get_max(a, i+1, size));
}
int get_sum(int *a, int i, int size)
{
if (i >= size)
return 0;
return a[i] + get_sum(a, i+1, size);
}
int get_partition(int *a, int size, int bound) {
int running_sum = 0;
int partitions = 0, i;
for (i=0;i<size;i++) {
if (a[i] + running_sum <= bound) {
running_sum += a[i];
} else {
running_sum = 0;
running_sum += a[i];
partitions++;
}
}
if (running_sum > 0)
partitions++;
return partitions;
}
int foo(int *a, int size, int k)
{
int lower = get_max(a, 0, size);
int higher = get_sum(a, 0, size);
int partition;
while (lower+1 < higher) {
int bound = (lower + (higher))/2;
partition = get_partition(a, size, bound);
printf("partition %d bound %d lower %d higher %d\n", partition, bound, lower, higher);
if (partition > k)
lower = bound;
else
higher = bound;
}
printf("partition %dlower %d higher %d\n", partition, lower, higher);
return higher;
}
#define SIZE(a) sizeof(a)/sizeof(a[0])
int main(void) {
int a[] = {2, 3, 4, 5, 6};
printf("%d\n", foo(a, SIZE(a), 3));
return 0;
}

Related

Longest Increasing Subarray after add or subtract some element an amount less than K

Given an array and we can add or subtract some element an amount less than K to make the longest increasing subarray
Example: An array a=[6,4,3,2] and K=1; we can subtract 1 from a[2]; add 1 to a[4] so the array will be a=[6,3,3,3] and the LIS is [3,3,3]
An algorithm of complexity O(n) is possible, by considering a "state" approach.
For each index i, the state corresponds to the three values that we can get: A[i]-K, A[i], A[i]+K.
Then, for a given index, for each state s = 0, 1, 2, we can calculate the maximum increasing sequence length terminating at this state.
length[i+1][s] = 1 + max (length[i][s'], if val[i][s'] <= val[i+1][s], for s' = 0, 1, 2)
We can use the fact that length[i][s] is increasing with s.
In practice, if we are only interesting to know the final maximum length, we don't need to memorize all the length values.
Here is a simple C++ implementation, to illustrate this algorithm. It only provides the maximum length.
#include <iostream>
#include <vector>
#include <array>
#include <string>
struct Status {
std::array<int, 3> val;
std::array<int, 3> l_seq; // length sequences
};
int longuest_ascending_seq (const std::vector<int>& A, int K) {
int max_length = 0;
int n = A.size();
if (n == 0) return 0;
Status previous, current;
previous = {{A[0]-K, A[0]-K, A[0]-K}, {0, 0, 0}};
for (int i = 0; i < n; ++i) {
current.val = {A[i]-K, A[i], A[i] + K};
for (int j = 0; j < 3; ++j) {
int x = current.val[j];
if (x >= previous.val[2]) {
current.l_seq[j] = previous.l_seq[2] + 1;
} else if (x >= previous.val[1]) {
current.l_seq[j] = previous.l_seq[1] + 1;
} else if (x >= previous.val[0]) {
current.l_seq[j] = previous.l_seq[0] + 1;
} else {
current.l_seq[j] = 1;
}
}
if (current.l_seq[2] > max_length) max_length = current.l_seq[2];
std::swap (previous, current);
}
return max_length;
}
int main() {
std::vector<int> A = {6, 4, 3, 2, 0};
int K = 1;
auto ans = longuest_ascending_seq (A, K);
std::cout << ans << std::endl;
return 0;
}

Find the missing number between [0, n] (n and numbers from 0 to n-1 are given by the user) using DAC

As a homework, I have to find the missing number from 0 to n using a divide and conquer (DAC) algorithm.
As an input, I get n-1 numbers from [0, n] and n.
I can easily do this with a quicksort and then just see which number is missing, but that would mean the complexity of my algorithm will be O(n*log n).
I'm wondering if there is any way I can do lower than that.
I was thinking that I might get the sum of the input (somehow) using DAC, and then the number missing will be n - sum. This would be O(n) complexity.
Is there any other way to get a complexity lower than O(n) (without using any space) and also, is my idea a good one? If not, can you give me other ideas for this problem, please?
Thanks.
Edit:
I know I should post another question, but I can post only once every 90 minutes (as I recall) and I want to finish this problem now if possible.
How can I calculate the sum of an array using DAC?
int DAC(int low, int high, int a[], int& s)
{
if (low <= high)
{
int pivot = (low + high)/2;
s += DAC(low, pivot - 1, a, s);
s += DAC(pivot+1, high, a, s);
return a[pivot];
}
}
for this call
cout << DAC(0, n-1, a, s);
Input:
7
1 2 3 4 5 6 7
I get 4 and I don't understand why. I didn't expect it to return only 4.
Edit 2:
I was getting call because I had to cout<<s, not DAC, I'm sorry.
Now I get 52 for the following code, with input: n=7, a=1 2 3 4 5 6 7
#include <iostream>
#include <algorithm>
using namespace std;
void citire(int& n, int a[])
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
}
int DAC(int low, int high, int a[], int& s)
{
if (low <= high)
{
int pivot = (low + high)/2;
s += DAC(low, pivot - 1, a, s);
s += DAC(pivot+1, high, a, s);
return a[pivot];
}
}
int main() {
int a[100], n, s = 0;
citire(n, a);
DAC(0, n-1, a, s);
cout << s;
return 0;
}
As sis modified internally, the function DAC doesn't have to return anything.
#include <iostream>
#include <algorithm>
void citire(int& n, int a[])
{
std::cin >> n;
for (int i = 0; i < n; i++)
{
std::cin >> a[i];
}
}
void DAC(int low, int high, int a[], int& s)
{
if (low <= high)
{
int pivot = (low + high)/2;
DAC(low, pivot - 1, a, s);
DAC(pivot+1, high, a, s);
s += a[pivot];
}
}
int main() {
int a[100], n, s = 0;
citire(n, a);
DAC(0, n-1, a, s);
std::cout << s << "\n";
return 0;
}
But std:accumulatewould be much simpler

Is every recursive algorithm a divide and conquer algorithm?

I have a problem for homework and I need to solve this problem with a divide and conquer algorithm.
I solved this algorithm by using recursion. Did I use divide and conquer automatically by using recursion?
For example, is this below approach a divide an conquer algorithm? Because I use fun function in fun.(recursive call)
Code:
#include <stdio.h>
/* int a[] = {-6,60,-10,20}; */
int a[] = {-2, -3, 4, -1, -2, 1, 5, -3};
int len = sizeof(a)/sizeof(*a);
int maxherearray[10];
int fun(int n);
int max(int a, int b);
int find_max(int a[], int len);
void print_array(int a[], int start_idx, int end_idx);
int start_idx = 0; // Start of contiguous subarray giving max sum
int end_idx = 0; // End of contiguous subarray giving max sum
#define NEG_INF (-100000)
int max_sum = NEG_INF; // The max cont sum seen so far.
int main(void)
{
start_idx = 0;
end_idx = len - 1;
maxherearray[0] = a[0];
printf("Array a[]: ");
print_array(a, 0, len-1);
printf("\n");
// Compute the necessary information to get max contiguous subarray
fun(len - 1);
printf("Max subarray value == %d\n", find_max(maxherearray, len));
printf("\n");
printf("Contiguous sums: ");
print_array(maxherearray, 0, len - 1);
printf("\n");
printf("Contiguous subarray giving max sum: ");
print_array(a, start_idx, end_idx);
printf("\n\n");
return 0;
}
int fun(int n)
{
if(n==0)
return a[0];
int max_till_j = fun(n - 1);
// Start of new contiguous sum
if (a[n] > a[n] + max_till_j)
{
maxherearray[n] = a[n];
if (maxherearray[n] > max_sum)
{
start_idx = end_idx = n;
max_sum = maxherearray[n];
}
}
// Add to current contiguous sum
else
{
maxherearray[n] = a[n] + max_till_j;
if (maxherearray[n] > max_sum)
{
end_idx = n;
max_sum = maxherearray[n];
}
}
return maxherearray[n];
}
int max(int a, int b)
{
return (a > b)? a : b;
}
// Print subarray a[i] to a[j], inclusive of end points.
void print_array(int a[], int i, int j)
{
for (; i <= j; ++i) {
printf("%d ", a[i]);
}
}
int find_max(int a[], int len)
{
int i;
int max_val = NEG_INF;
for (i = 0; i < len; ++i)
{
if (a[i] > max_val)
{
max_val = a[i];
}
}
return max_val;
}
Every recursion function is not necessarily divide-and-conquer approach. There are other approaches like decrease-and-conquer(decrease by a constant factor, decrease by one, variable-size decrease).
Is this below approach a divide an conquer algorithm?
Your function is exactly decrease by a constant factor which is 1 approach. You can glance at here.
Pseudocode for the divide-and-conquer algorithm for
finding a maximum-subarray
MaxSubarray(A,low,high)
//
if high == low
return (low, high, A[low]) // base case: only one element
else
// divide and conquer
mid = floor( (low + high)/2 )
(leftlow,lefthigh,leftsum) = MaxSubarray(A,low,mid)
(rightlow,righthigh,rightsum) = MaxSubarray(A,mid+1,high)
(xlow,xhigh,xsum) = MaxXingSubarray(A,low,mid,high)
// combine
if leftsum >= rightsum and leftsum >= xsum
return (leftlow,lefthigh,leftsum)
else if rightsum >= leftsum and rightsum >= xsum
return (rightlow,righthigh,rightsum)
else
return (xlow,xhigh,xsum)
end if
end if
--------------------------------------------------------------
MaxXingSubarray(A,low,mid,high)
// Find a max-subarray of A[i..mid]
leftsum = -infty
sum = 0
for i = mid downto low
sum = sum + A[i]
if sum > leftsum
leftsum = sum
maxleft = i
end if
end for
// Find a max-subarray of A[mid+1..j]
rightsum = -infty
sum = 0
for j = mid+1 to high
sum = sum + A[j]
if sum > rightsum
rightsum = sum
maxright = j
end if
end for
// Return the indices i and j and the sum of the two subarrays
return (maxleft,maxright,leftsum + rightsum)
-----------------------------------------------------------
=== Remarks:
(1) Initial call: MaxSubarray(A,1,n)
(2) Divide by computing mid.
Conquer by the two recursive alls to MaxSubarray.
Combine by calling MaxXingSubarray and then determining
which of the three results gives the maximum sum.
(3) Base case is when the subarray has only 1 element.
Not necessarily. If you explore the functional programming paradigm you will learn that the simple for loop can be replaced with recursion
for i in range(x):
body(i)
changes to
def do_loop(x, _start=0):
if _start < x:
body(_start)
do_loop(x, _start=_start+1)
and it's quite obvious that not every iteration is a divide and conquer algorithm.

Mergesort implementation is slow

I'am doing a report about different sorting algorithms in C++. What baffles me is that my mergesort seems to be slower than heapsort in both of the languages. What I've seen is that heapsort is supposed to be slower.
My mergesort sorts an unsorted array with size 100000 at a speed of 19.8 ms meanwhile heapsort sorts it at 9.7 ms. The code for my mergesort function in C++ is as follows:
void merge(int *array, int low, int mid, int high) {
int i, j, k;
int lowLength = mid - low + 1;
int highLength = high - mid;
int *lowArray = new int[lowLength];
int *highArray = new int[highLength];
for (i = 0; i < lowLength; i++)
lowArray[i] = array[low + i];
for (j = 0; j < highLength; j++)
highArray[j] = array[mid + 1 + j];
i = 0;
j = 0;
k = low;
while (i < lowLength && j < highLength) {
if (lowArray[i] <= highArray[j]) {
array[k] = lowArray[i];
i++;
} else {
array[k] = highArray[j];
j++;
}
k++;
}
while (i < lowLength) {
array[k] = lowArray[i];
i++;
k++;
}
while (j < highLength) {
array[k] = highArray[j];
j++;
k++;
}
}
void mergeSort(int *array, int low, int high) {
if (low < high) {
int mid = low + (high - low) / 2;
mergeSort(array, low, mid);
mergeSort(array, mid + 1, high);
merge(array, low, mid, high);
}
}
The example merge sort is doing allocation and copying of data in merge(), and both can be eliminated with a more efficient merge sort. A single allocation for the temp array can be done in a helper / entry function, and the copy is avoided by changing the direction of merge depending on level of recursion either by using two mutually recursive functions (as in example below) or with a boolean parameter.
Here is an example of a C++ top down merge sort that is reasonably optimized. A bottom up merge sort would be slightly faster, and on a system with 16 registers, a 4 way bottom merge sort a bit faster still, about as fast or faster than quick sort.
// prototypes
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee);
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee);
void TopDownMerge(int a[], int b[], size_t ll, size_t rr, size_t ee);
void MergeSort(int a[], size_t n) // entry function
{
if(n < 2) // if size < 2 return
return;
int *b = new int[n];
TopDownSplitMergeAtoA(a, b, 0, n);
delete[] b;
}
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee)
{
if((ee - ll) == 1) // if size == 1 return
return;
size_t rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoB(a, b, ll, rr);
TopDownSplitMergeAtoB(a, b, rr, ee);
TopDownMerge(b, a, ll, rr, ee); // merge b to a
}
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee)
{
if((ee - ll) == 1){ // if size == 1 copy a to b
b[ll] = a[ll];
return;
}
size_t rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoA(a, b, ll, rr);
TopDownSplitMergeAtoA(a, b, rr, ee);
TopDownMerge(a, b, ll, rr, ee); // merge a to b
}
void TopDownMerge(int a[], int b[], size_t ll, size_t rr, size_t ee)
{
size_t o = ll; // b[] index
size_t l = ll; // a[] left index
size_t r = rr; // a[] right index
while(1){ // merge data
if(a[l] <= a[r]){ // if a[l] <= a[r]
b[o++] = a[l++]; // copy a[l]
if(l < rr) // if not end of left run
continue; // continue (back to while)
while(r < ee) // else copy rest of right run
b[o++] = a[r++];
break; // and return
} else { // else a[l] > a[r]
b[o++] = a[r++]; // copy a[r]
if(r < ee) // if not end of right run
continue; // continue (back to while)
while(l < rr) // else copy rest of left run
b[o++] = a[l++];
break; // and return
}
}
}

Large values in segment tree for product range queries

I wrote code for firing product range queries on an array.
Note: This question not a duplicate to Multiplication in a range. My problem is something different
I wrote the code for this,
// Program to show segment tree operations like construction, query and update
#include <stdio.h>
#include <math.h>
#define R 1000000000
typedef unsigned long long ull;
int getMid(int s, int e) { return s + (e -s)/2; }
ull getProdUtil(ull *st, int ss, int se, int qs, int qe, int index)
{
if (qs <= ss && qe >= se)
return st[index];
if (se < qs || ss > qe)
return 1;
int mid = getMid(ss, se);
return (getProdUtil(st, ss, mid, qs, qe, 2*index+1) %R *
getProdUtil(st, mid+1, se, qs, qe, 2*index+2) % R )%R;
}
ull getProd(ull *st, int n, int qs, int qe)
{
if (qs < 0 || qe > n-1 || qs > qe)
{
return 0;
}
return getProdUtil(st, 0, n-1, qs, qe, 0) % R;
}
ull constructSTUtil(ull arr[], int ss, int se, ull *st, int si)
{
if (ss == se)
{
st[si] = arr[ss];
return arr[ss];
}
int mid = getMid(ss, se);
ull l = (ull)constructSTUtil(arr, ss, mid, st, si*2+1)%R;
ull r = (ull)constructSTUtil(arr, mid+1, se, st, si*2+2)%R;
st[si] = (l * r) % R;
return st[si];
}
void init_array(ull *st, ull size){
for(ull i=0;i< size;i++){
st[i] = 1;
}
}
ull *constructST(ull arr[], int n)
{
int x = (int)(ceil(log2(n))); //Height of segment tree
int max_size = 2*(int)pow(2, x) - 1; //Maximum size of segment tree
ull *st = new ull[max_size];
init_array(st, max_size);
constructSTUtil(arr, 0, n-1, st, 0);
return st;
}
void print_array(ull *array,int size){
printf("\n");
for(int i=0; i< size;i++){
printf("<%d,%llu> ", i+1,array[i]);
}
}
int main()
{
int n;
scanf("%d",&n);
ull arr[n];
for(int i=0;i<n;i++){
scanf("%llu",&arr[i]);
}
ull *st = constructST(arr, n);
int t;
scanf("%d", &t);
while(t--){
int l,m,r;
scanf("%d %d %d", &l,&r,&m);
ull result = getProd(st, n, l-1, r-1);
printf("%lld\n",result % m);
//int sizes = 100;
//printf("%llu\n",st[0]);
//print_array(st, sizes);
}
return 0;
}
The constraints are,
1 ≤ N ≤ 100,000 (size of array)
1 ≤ Ai ≤ 100 (array elements)
1 ≤ T ≤ 100,000 (number of test cases)
1 ≤ Li ≤ Ri ≤ N (Left and right ranges in query)
1 ≤ Mi ≤ 10<sup>9</sup> (Modulus for each test case. Each test case has to be modulu of this number)
Im pretty sure that R , that I have chosen is failing some test case. I tried R with 1018 as well. But still same problem. Dont know why this is happening?
My question is , is it the problem of R I have chosen or is it the problem of different M being passed in each test case.
Note: Truly not expecting a solution, just expecting a clue
Regards

Resources