Hello guys I am having the following problem:
I have an array with a lenght that is a multiple of 4 e.g:
{1,2,3,4,5,6,7,8}
I want to know how can i get the numbers in the following pairs: {1,4},{2,3},{5,8},{6,7}.....(etc)
Suppose i loop through them and i want to get the index of the pair member from my current index
int myarr[8]={1,2,3,4,5,6,7,8};
for(int i=0;i<8;i++)
**j= func(i)**
I have thought of something like this:
f(1)=4
f(4)=1
and i would be taking: **f(i)=a * i + b** (i think a linear function is enough) It would result: f(i)=j=-i+5 .How can i generalise this for more then 4 members? What do you do in cases where you need a general formula for pairing elements?
Basically, if i is odd j would be i+3, otherwise j = i+1;
int func(int i) {
if(i%2 != 0)
return i+3;
else
return i+1;
}
This will generate
func(1) = 4, func(2) = 3, func(5) = 8, func(6) = 7 // {1,4},{2,3},{5,8},{6,7}.
You could do it as follows by keeping the incremental iteration but use a function depending on the current block and the remainder as follows.
int myarr[8]={1,2,3,4,5,6,7,8};
int Successor(int i)
{
int BlockStart = i / 4;
int Remainder = i % 4;
int j = 0;
if ( Remainder == 0 )
j = 0;
else if ( Remainder == 1 )
j = 3;
else if ( Remainder == 2 )
j = 1;
else if ( Remainder == 3 )
j = 2
return BlockStart + j;
}
for(int i = 0; i < 8; i++)
{
j = f(i);
// usage of the index
}
About the generalization, this should do it:
auto pairs(const vector<int>& in, int groupLength = 4) {
vector<pair<int, int>> result;
int groups = in.size() / groupLength;
for (int group = 0; group < groups; ++group) {
int i = group * groupLength;
int j = i + groupLength - 1;
while (i < j) {
result.emplace_back(in[i++], in[j--]);
}
}
return result;
}
You can run this code online.
If you are just looking for a formula to calculate the indices, then in general case it's:
int f(int i, int k = 4) {
return i + k - 2 * (i % k) - 1;
}
Turns out your special case (size 4) is sequence A004444 in OEIS.
In general you have "nimsum n + (size-1)".
Related
I'm looking for the optimal method to find the maximum value of sub-list items in a list.
Here is my O(n.m) implementation:
vector<int> movMax(const vector<int>& v, int span)
{
span /= 2;
vector<int> ret = v;
for (int i = 0; i < (int)v.size(); ++i)
{
for (int j = std::max(0, i - span); j < std::min((int)v.size(), i + span + 1); j++)
{
ret[i] = std::max(ret[i], v[j]);
}
}
return ret;
}
int main()
{
vector<int> v = { 4, 3, 3, 7, 2, 5, 1, 2 };
v = movMax(v, 3);
for (int x : v) cout << x << ' '; // 4 4 7 7 7 5 5 2
}
Here's a version that's O(N log M), where N is the size of the input, and M is the window.
We keep a track of the values within the window in-order, so adding or removing them is O(log M), and we do this for each element of v.
std::vector<int> movMax(const std::vector<int>& v, int window)
{
int mid = window / 2;
int size = v.size();
std::vector<int> result;
result.reserve(size);
std::multiset<int> working_set;
for (int i = -mid; i < size + mid; ++i)
{
if (i + mid < size) working_set.insert(v.at(i + mid));
if (i >= 0 && i < size) result.push_back(*working_set.rbegin());
if (i - mid >= 0) working_set.erase(working_set.find(v.at(i - mid)));
}
return result;
}
See it on coliru
If window is allowed to be even, you need to account for which side it prefers.
Instead of defining one mid, you have wide and narrow:
int wide = window / 2;
int narrow = window - wide - 1;
Assuming window should span to front more:
for (int i = -narrow; i < size + wide; ++i)
{
if (i + narrow < size) working_set.insert(v.at(i + narrow));
if (i >= 0 && i < size) result.push_back(*working_set.rbegin());
if (i - wide >= 0) working_set.erase(working_set.find(v.at(i - wide)));
}
Assuming window should span to back more:
for (int i = -wide; i < size + narrow; ++i)
{
if (i + wide < size) working_set.insert(v.at(i + wide));
if (i >= 0 && i < size) result.push_back(*working_set.rbegin());
if (i - narrow >= 0) working_set.erase(working_set.find(v.at(i - narrow)));
}
With tests on godbolt
I tried this Codility test: MinAbsSum.
https://codility.com/programmers/lessons/17-dynamic_programming/min_abs_sum/
I solved the problem by searching the whole tree of possibilities. The results were OK, however, my solution failed due to timeout for large input. In other words the time complexity was not as good as expected. My solution is O(nlogn), something normal with trees. But this coding test was in the section "Dynamic Programming", and there must be some way to improve it. I tried with summing the whole set first and then using this information, but always there is something missing in my solution. Does anybody have an idea on how to improve my solution using DP?
#include <vector>
using namespace std;
int sum(vector<int>& A, size_t i, int s)
{
if (i == A.size())
return s;
int tmpl = s + A[i];
int tmpr = s - A[i];
return min (abs(sum(A, i+1, tmpl)), abs(sum(A, i+1, tmpr)));
}
int solution(vector<int> &A) {
return sum(A, 0, 0);
}
I could not solve it. But here's the official answer.
Quoting it:
Notice that the range of numbers is quite small (maximum 100). Hence,
there must be a lot of duplicated numbers. Let count[i] denote the
number of occurrences of the value i. We can process all occurrences
of the same value at once. First we calculate values count[i] Then we
create array dp such that:
dp[j] = −1 if we cannot get the sum j,
dp[j] >= 0 if we can get sum j.
Initially, dp[j] = -1 for all of j (except dp[0] = 0). Then we scan
through all the values a appearing in A; we consider all a such
that count[a]>0. For every such a we update dp that dp[j] denotes
how many values a remain (maximally) after achieving sum j. Note
that if the previous value at dp[j] >= 0 then we can set dp[j] =
count[a] as no value a is needed to obtain the sum j. Otherwise we
must obtain sum j-a first and then use a number a to get sum j. In
such a situation dp[j] = dp[j-a]-1. Using this algorithm, we can
mark all the sum values and choose the best one (closest to half of S,
the sum of abs of A).
def MinAbsSum(A):
N = len(A)
M = 0
for i in range(N):
A[i] = abs(A[i])
M = max(A[i], M)
S = sum(A)
count = [0] * (M + 1)
for i in range(N):
count[A[i]] += 1
dp = [-1] * (S + 1)
dp[0] = 0
for a in range(1, M + 1):
if count[a] > 0:
for j in range(S):
if dp[j] >= 0:
dp[j] = count[a]
elif (j >= a and dp[j - a] > 0):
dp[j] = dp[j - a] - 1
result = S
for i in range(S // 2 + 1):
if dp[i] >= 0:
result = min(result, S - 2 * i)
return result
(note that since the final iteration only considers sums up until S // 2 + 1, we can save some space and time by only creating a DP Cache up until that value as well)
The Java answer provided by fladam returns wrong result for input [2, 3, 2, 2, 3], although it gets 100% score.
Java Solution
import java.util.Arrays;
public class MinAbsSum{
static int[] dp;
public static void main(String args[]) {
int[] array = {1, 5, 2, -2};
System.out.println(findMinAbsSum(array));
}
public static int findMinAbsSum(int[] A) {
int arrayLength = A.length;
int M = 0;
for (int i = 0; i < arrayLength; i++) {
A[i] = Math.abs(A[i]);
M = Math.max(A[i], M);
}
int S = sum(A);
dp = new int[S + 1];
int[] count = new int[M + 1];
for (int i = 0; i < arrayLength; i++) {
count[A[i]] += 1;
}
Arrays.fill(dp, -1);
dp[0] = 0;
for (int i = 1; i < M + 1; i++) {
if (count[i] > 0) {
for(int j = 0; j < S; j++) {
if (dp[j] >= 0) {
dp[j] = count[i];
} else if (j >= i && dp[j - i] > 0) {
dp[j] = dp[j - i] - 1;
}
}
}
}
int result = S;
for (int i = 0; i < Math.floor(S / 2) + 1; i++) {
if (dp[i] >= 0) {
result = Math.min(result, S - 2 * i);
}
}
return result;
}
public static int sum(int[] array) {
int sum = 0;
for(int i : array) {
sum += i;
}
return sum;
}
}
I invented another solution, better than the previous one. I do not use recursion any more.
This solution works OK (all logical tests passed), and also passed some of the performance tests, but not all. How else can I improve it?
#include <vector>
#include <set>
using namespace std;
int solution(vector<int> &A) {
if (A.size() == 0) return 0;
set<int> sums, tmpSums;
sums.insert(abs(A[0]));
for (auto it = begin(A) + 1; it != end(A); ++it)
{
for (auto s : sums)
{
tmpSums.insert(abs(s + abs(*it)));
tmpSums.insert(abs(s - abs(*it)));
}
sums = tmpSums;
tmpSums.clear();
}
return *sums.begin();
}
This solution (in Java) scored 100% for both (correctness and performance)
public int solution(int[] a){
if (a.length == 0) return 0;
if (a.length == 1) return a[0];
int sum = 0;
for (int i=0;i<a.length;i++){
sum += Math.abs(a[i]);
}
int[] indices = new int[a.length];
indices[0] = 0;
int half = sum/2;
int localSum = Math.abs(a[0]);
int minLocalSum = Integer.MAX_VALUE;
int placeIndex = 1;
for (int i=1;i<a.length;i++){
if (localSum<half){
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
localSum += Math.abs(a[i]);
indices[placeIndex++] = i;
}else{
if (localSum == half)
return Math.abs(2*half - sum);
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
if (placeIndex > 1) {
localSum -= Math.abs(a[indices[placeIndex--]]);
i = indices[placeIndex];
}
}
}
return (Math.abs(2*minLocalSum - sum));
}
this solution treats all elements like they are positive numbers and it's looking to reach as close as it can to the sum of all elements divided by 2 (in that case we know that the sum of all other elements will be the same delta far from the half too -> abs sum will be minimum possible ).
it does so by starting with the first element and successively adding others to the "local" sum (and recording indices of elements in the sum) until it reaches sum of x >= sumAll/2. if that x is equal to sumAll/2 we have an optimal solution. if not, we go step back in the indices array and continue picking other element where last iteration in that position ended. the result will be a "local" sum having abs((sumAll - sum) - sum) closest to 0;
fixed solution:
public static int solution(int[] a){
if (a.length == 0) return 0;
if (a.length == 1) return a[0];
int sum = 0;
for (int i=0;i<a.length;i++) {
a[i] = Math.abs(a[i]);
sum += a[i];
}
Arrays.sort(a);
int[] arr = a;
int[] arrRev = new int[arr.length];
int minRes = Integer.MAX_VALUE;
for (int t=0;t<=4;t++) {
arr = fold(arr);
int res1 = findSum(arr, sum);
if (res1 < minRes) minRes = res1;
rev(arr, arrRev);
int res2 = findSum(arrRev, sum);
if (res2 < minRes) minRes = res2;
arrRev = fold(arrRev);
int res3 = findSum(arrRev, sum);
if (res3 < minRes) minRes = res3;
}
return minRes;
}
private static void rev(int[] arr, int[] arrRev){
for (int i = 0; i < arrRev.length; i++) {
arrRev[i] = arr[arr.length - 1 - i];
}
}
private static int[] fold(int[] a){
int[] arr = new int[a.length];
for (int i=0;a.length/2+i/2 < a.length && a.length/2-i/2-1 >= 0;i+=2){
arr[i] = a[a.length/2+i/2];
arr[i+1] = a[a.length/2-i/2-1];
}
if (a.length % 2 > 0) arr[a.length-1] = a[a.length-1];
else{
arr[a.length-2] = a[0];
arr[a.length-1] = a[a.length-1];
}
return arr;
}
private static int findSum(int[] arr, int sum){
int[] indices = new int[arr.length];
indices[0] = 0;
double half = Double.valueOf(sum)/2;
int localSum = Math.abs(arr[0]);
int minLocalSum = Integer.MAX_VALUE;
int placeIndex = 1;
for (int i=1;i<arr.length;i++){
if (localSum == half)
return 2*localSum - sum;
if (Math.abs(2*minLocalSum-sum) > Math.abs(2*localSum - sum))
minLocalSum = localSum;
if (localSum<half){
localSum += Math.abs(arr[i]);
indices[placeIndex++] = i;
}else{
if (placeIndex > 1) {
localSum -= Math.abs(arr[indices[--placeIndex]]);
i = indices[placeIndex];
}
}
}
return Math.abs(2*minLocalSum - sum);
}
The following is a rendering of the official answer in C++ (scoring 100% in task, correctness, and performance):
#include <cmath>
#include <algorithm>
#include <numeric>
using namespace std;
int solution(vector<int> &A) {
// write your code in C++14 (g++ 6.2.0)
const int N = A.size();
int M = 0;
for (int i=0; i<N; i++) {
A[i] = abs(A[i]);
M = max(M, A[i]);
}
int S = accumulate(A.begin(), A.end(), 0);
vector<int> counts(M+1, 0);
for (int i=0; i<N; i++) {
counts[A[i]]++;
}
vector<int> dp(S+1, -1);
dp[0] = 0;
for (int a=1; a<M+1; a++) {
if (counts[a] > 0) {
for (int j=0; j<S; j++) {
if (dp[j] >= 0) {
dp[j] = counts[a];
} else if ((j >= a) && (dp[j-a] > 0)) {
dp[j] = dp[j-a]-1;
}
}
}
}
int result = S;
for (int i =0; i<(S/2+1); i++) {
if (dp[i] >= 0) {
result = min(result, S-2*i);
}
}
return result;
}
You are almost 90% to the actual solution. It seems you understand recursion very well. Now, You should apply dynamic programming here with your program.
Dynamic Programming is nothing but memoization to the recursion so that we will not calculate same sub problems again and again. If same sub problems encounter , we return the previously calculated and memorized value. Memorization can be done with the help of a 2D array , say dp[][], where first state represent current index of array and second state represent summation.
For this problem specific, instead of giving calls to both states from each state, you sometimes can greedily take decision to skip one call.
I would like to provide the algorithm and then my implementation in C++. Idea is more or less the same as the official codility solution with some constant optimisation added.
Calculate the maximum absolute element of the inputs.
Calculate the absolute sum of the inputs.
Count the number of occurrence of each number in the inputs. Store the results in a vector hash.
Go through each input.
For each input, goes through all possible sums of any number of inputs. It is a slight constant optimisation to go only up to half of the possible sums.
For each sum that has been made before, set the occurrence count of the current input.
Check for each potential sum equal to or greater than the current input whether this input has already been used before. Update the values at the current sum accordingly. We do not need to check for potential sums less than the current input in this iteration, since it is evident that it has not been used before.
The above nested loop will fill in each possible sum with a value greater than -1.
Go through this possible sum hash again to look for the closest sum to half that is possible to make. Eventually, the min abs sum will be the difference of this from the half multiplied by two as the difference will be added up in both groups as the difference from the median.
The runtime complexity of this algorithm is O(N * max(abs(A)) ^ 2), or simply O(N * M ^ 2). That is because the outer loop is iterating M times and the inner loop is iterating sum times. The sum is basically N * M in worst case. Therefore, it is O(M * N * M).
The space complexity of this solution is O(N * M) because we allocate a hash of N items for the counts and a hash of S items for the sums. S is N * M again.
int solution(vector<int> &A)
{
int M = 0, S = 0;
for (const int e : A) { M = max(abs(e), M); S += abs(e); }
vector<int> counts(M + 1, 0);
for (const int e : A) { ++counts[abs(e)]; }
vector<int> sums(S + 1, -1);
sums[0] = 0;
for (int ci = 1; ci < counts.size(); ++ci) {
if (!counts[ci]) continue;
for (int si = 0; si < S / 2 + 1; ++si) {
if (sums[si] >= 0) sums[si] = counts[ci];
else if (si >= ci and sums[si - ci] > 0) sums[si] = sums[si - ci] - 1;
}
}
int min_abs_sum = S;
for (int i = S / 2; i >= 0; --i) if (sums[i] >= 0) return S - 2 * i;
return min_abs_sum;
}
Let me add my 50 cent, how to come up with the score 100% solution.
For me it was hard to understand the ultimate solution, proposed earlier in this thread.
So I started with warm-up solution with score 63%, because its O(NxNxM),
and because it doesn't use the fact that M is quite small value, and there are many duplicates in big arrays
here the key part is to understand how array isSumPossible is filled and interpreted:
how to fill array isSumPossible using numbers in input array:
if isSumPossible[sum] >= 0, i.e. sum is already possible, even without current number, then let's set it's value to 1 - count of current number, that is left unused for this sum, it'll go to our "reserve", so we can use it later for greater sums.
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
}
if isSumPossible[sum] <= 0, i.e. sum is considered not yet possible, with all input numbers considered previously, then let's check maybe
smaller sum sum - number is already considered as possible, and we have in "reserve" our current number (isSumPossible[sum - number] == 1), then do following
else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
here isSumPossible[sum] = 0 means that we have used number in composing sum and it's now considered as possible (>=0), but we have no number in "reserve", because we've used it ( =0)
how to interpret filled array isSumPossible after considering all numbers in input array:
if isSumPossible[sum] >= 0 then the sum is possible, i.e. it can be reached by summation of some numbers in given array
if isSumPossible[sum] < 0 then the sum can't be reached by summation of any numbers in given array
The more simple thing here is to understand why we are searching sums only in interval [0, maxSum/2]:
because if find a possible sum, that is very close to maxSum/2,
ideal case here if we've found possible sum = maxSum/2,
if so, then it's obvious, that we can somehow use the rest numbers in input array to make another maxSum/2, but now with negative sign, so as a result of annihilation we'll get solution = 0, because maxSum/2 + (-1)maxSum/2 = 0.
But 0 the best case solution, not always reachable.
But we, nevertheless, should seek for the minimal delta = ((maxSum - sum) - sum),
so this we seek for delta -> 0, that's why we have this:
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, (maxSum - sum) - sum);
}
}
warm-up solution
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxSum += A[i];
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number : A) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
} else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
and after this we can optimize it, using the fact that there are many duplicate numbers in big arrays, and we come up with the solution with 100% score, its O(Mx(NxM)), because maxSum = NxM at worst case
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxNumber = 0;
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxNumber = Math.max(maxNumber, A[i]);
maxSum += A[i];
}
int[] count = new int[maxNumber + 1];
for (int i = 0; i < A.length; i++) {
count[A[i]]++;
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number = 0; number < maxNumber + 1; number++) {
if (count[number] > 0) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = count[number];
} else if (sum >= number && isSumPossible[sum - number] > 0) {
isSumPossible[sum] = isSumPossible[sum - number] - 1;
}
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
I hope I've made it at least a little clear
Kotlin solution
Time complexity: O(N * max(abs(A))**2)
Score: 100%
import kotlin.math.*
fun solution(A: IntArray): Int {
val N = A.size
var M = 0
for (i in 0 until N) {
A[i] = abs(A[i])
M = max(M, A[i])
}
val S = A.sum()
val counts = MutableList(M + 1) { 0 }
for (i in 0 until N) {
counts[A[i]]++
}
val dp = MutableList(S + 1) { -1 }
dp[0] = 0
for (a in 1 until M + 1) {
if (counts[a] > 0) {
for (j in 0 until S) {
if (dp[j] >= 0) {
dp[j] = counts[a]
} else if (j >= a && dp[j - a] > 0) {
dp[j] = dp[j - a] - 1
}
}
}
}
var result = S
for (i in 0 until (S / 2 + 1)) {
if (dp[i] >= 0) {
result = minOf(result, S - 2 * i)
}
}
return result
}
Given : A two dimensional array , values K and M
Problem : Find the maximum possible sum less than or equal K using all the rows (i.e there should be an element form each row) using exactly M elements.
This is a snippet of a program, I am having trouble implementing the conditions for each row and M.
for (int i = 0 ; i<n ; i++)
for (int s=0; s<M; s++)
for (int j=K;j>=0;j--)
if (dp[s][j] && A[i] + j < K)
dp[s + 1][j + A[i]] = true;
EDIT 1: Rows = M , i.e one element from each row has to be selected.
EDIT 2 : Dynamic Programming Solution, Thanks to #6502
ill ret(V(ill) col[101],ill prec[][101],ill s,ill row,ill m,ill k)
{
if(prec[s][row])
return prec[s][row];
else
{
if(row==m+1)
return s;
ill best=-1;
int j=row;
for(int i=0;i<col[j].size();i++)
{
if(s+col[j][i] <= k)
{
ill x = ret (col,prec,s+col[j][i],row+1,m,k);
if ((best==-1)||(x>best))
best=x;
}
}
prec[s][row]=best;
return best;
}
}
The problem can be solved using dynamic programming by choosing as state the pair (s, row) where s is the current sum and row is the next row we need to include.
The maximal principle is valid because no matter on which choices we made in previous rows the result depends only on the current sum and the current row index.
In code (Python)
cache = {}
data = [[2, 3, 4],
[2, 3, 4],
[2, 3, 4]]
M = 3
K = 10
def msum(s, row):
try:
return cache[s, row]
except KeyError:
if row == M:
return s
best = None
for v in data[row]:
if s+v <= K:
x = msum(s+v, row+1)
if best is None or x > best:
best = x
cache[s, row] = best
return best
print msum(0, 0)
The function returns None if no solution exists (i.e. if even taking the smallest value from each row we end up exceeding K).
A brute force approach:
bool increase(const std::vector<std::vector<int>>& v, std::vector<std::size_t>& it)
{
for (std::size_t i = 0, size = it.size(); i != size; ++i) {
const std::size_t index = size - 1 - i;
++it[index];
if (it[index] > v[index].size()) {
it[index] = 0;
} else {
return true;
}
}
return false;
}
int sum(const std::vector<std::vector<int>>& v, const std::vector<std::size_t>& it)
{
int res = 0;
for (std::size_t i = 0; i != it.size(); ++i) {
res += v[i][it[i]];
}
return res;
}
int maximum_sum_less_or_equal_to_K(const std::vector<std::vector<int>>& v, int K)
{
std::vector<std::size_t> it(v.size());
int res = K + 1;
do {
int current_sum = sum(v, it);
if (current_sum <= K) {
if (res == K + 1 || res < current_sum) {
res = current_sum;
}
}
} while (increase(v, it));
if (res == K + 1) {
// Handle no solution
}
return res;
}
it has the current selection of each row.
This can be solved using boolean 2D table. The value of dp[r][s] is set to true, if its possible to generate sum 's' , using exactly 'r' rows (i.e exactly one element from each of the [0 to r-1] rows). Using this dp table, we can compute next state as
dp[r+1][s] |= dp[r][s-A[r+1][c]] ; 0 < c < N, 0 < s <= K
where N is number of columns(0-based indexing). Finally return the value of max index set in M-1 row of dp table
Following is a bottom-up implementation
// Assuming input matrix is M*N
int maxSum() {
memset(dp, false, sizeof(dp));
//Initialise base row
for (int c = 0; c < N; ++c)
dp[0][A[0][c]] = true;
for ( int r = 1; r < M; ++r ) {
for ( int c = 0; c < N; ++c) {
// For each A[r][c], check for all possible values of sum upto K
for (int sum = 0; sum <= K; ++sum) {
if ( sum-A[r][c] >= 0 && dp[r-1][sum-A[r][c]] )
dp[r][sum] = true;
}
}
}
// Return max possible value <= K
for (int sum = K; sum >= 0; --sum) {
if ( dp[M-1][sum] )
return sum;
}
return 0;
}
Note that dp table values for current row depend only on previous row, as such space optimization trick can be used to solve it using 1-D table
I stumbled upon this problem on Codility Lessons, here is the description:
A non-empty zero-indexed array A consisting of N integers is given.
A triplet (X, Y, Z), such that 0 ≤ X < Y < Z < N, is called a double slice.
The sum of double slice (X, Y, Z) is the total of A[X + 1] + A[X + 2] + ... + A[Y − 1] + A[Y + 1] + A[Y + 2] + ... + A[Z − 1].
For example, array A such that:
A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2
contains the following example double slices:
double slice (0, 3, 6), sum is 2 + 6 + 4 + 5 = 17,
double slice (0, 3, 7), sum is 2 + 6 + 4 + 5 − 1 = 16,
double slice (3, 4, 5), sum is 0.
The goal is to find the maximal sum of any double slice.
Write a function:
int solution(vector &A);
that, given a non-empty zero-indexed array A consisting of N integers, returns the maximal sum of any double slice.
For example, given:
A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2
the function should return 17, because no double slice of array A has a sum of greater than 17.
Assume that:
N is an integer within the range [3..100,000];
each element of array A is an integer within the range [−10,000..10,000].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(N), beyond input storage (not counting >the storage required for input arguments).
Elements of input arrays can be modified.
I have already read about the algorithm with counting MaxSum starting at index i and ending at index i, but I don't know why my approach sometimes gives bad results. The idea is to compute MaxSum ending at index i, ommiting the minimum value at range 0..i. And here is my code:
int solution(vector<int> &A) {
int n = A.size();
int end = 2;
int ret = 0;
int sum = 0;
int min = A[1];
while (end < n-1)
{
if (A[end] < min)
{
sum = max(0, sum + min);
ret = max(ret, sum);
min = A[end];
++end;
continue;
}
sum = max(0, sum + A[end]);
ret = max(ret, sum);
++end;
}
return ret;
}
I would be glad if you could help me point out the loophole!
My solution based on bidirectional Kadane's algorithm. More details on my blog here. Scores 100/100.
public int solution(int[] A) {
int N = A.length;
int[] K1 = new int[N];
int[] K2 = new int[N];
for(int i = 1; i < N-1; i++){
K1[i] = Math.max(K1[i-1] + A[i], 0);
}
for(int i = N-2; i > 0; i--){
K2[i] = Math.max(K2[i+1]+A[i], 0);
}
int max = 0;
for(int i = 1; i < N-1; i++){
max = Math.max(max, K1[i-1]+K2[i+1]);
}
return max;
}
Here is my code:
int get_max_sum(const vector<int>& a) {
int n = a.size();
vector<int> best_pref(n);
vector<int> best_suf(n);
//Compute the best sum among all x values assuming that y = i.
int min_pref = 0;
int cur_pref = 0;
for (int i = 1; i < n - 1; i++) {
best_pref[i] = max(0, cur_pref - min_pref);
cur_pref += a[i];
min_pref = min(min_pref, cur_pref);
}
//Compute the best sum among all z values assuming that y = i.
int min_suf = 0;
int cur_suf = 0;
for (int i = n - 2; i > 0; i--) {
best_suf[i] = max(0, cur_suf - min_suf);
cur_suf += a[i];
min_suf = min(min_suf, cur_suf);
}
//Check all y values(y = i) and return the answer.
int res = 0;
for (int i = 1; i < n - 1; i++)
res = max(res, best_pref[i] + best_suf[i]);
return res;
}
int get_max_sum_dummy(const vector<int>& a) {
//Try all possible values of x, y and z.
int res = 0;
int n = a.size();
for (int x = 0; x < n; x++)
for (int y = x + 1; y < n; y++)
for (int z = y + 1; z < n; z++) {
int cur = 0;
for (int i = x + 1; i < z; i++)
if (i != y)
cur += a[i];
res = max(res, cur);
}
return res;
}
bool test() {
//Generate a lot of small test cases and compare the output of
//a brute force and the actual solution.
bool ok = true;
for (int test = 0; test < 10000; test++) {
int size = rand() % 20 + 3;
vector<int> a(size);
for (int i = 0; i < size; i++)
a[i] = rand() % 20 - 10;
if (get_max_sum(a) != get_max_sum_dummy(a))
ok = false;
}
for (int test = 0; test < 10000; test++) {
int size = rand() % 20 + 3;
vector<int> a(size);
for (int i = 0; i < size; i++)
a[i] = rand() % 20;
if (get_max_sum(a) != get_max_sum_dummy(a))
ok = false;
}
return ok;
}
The actual solution is get_max_sum function(the other two are a brute force solution and a tester functions that generates a random array and compares the output of a brute force and actual solution, I used them for testing purposes only).
The idea behind my solution is to compute the maximum sum in a sub array that that starts somewhere before i and ends in i - 1, then do the same thing for suffices(best_pref[i] and best_suf[i], respectively). After that I just iterate over all i and return the best value of best_pref[i] + best_suf[i]. It works correctly because best_pref[y] finds the best x for a fixed y, best_suf[y] finds the best z for a fixed y and all possible values of y are checked.
def solution(A):
n = len(A)
K1 = [0] * n
K2 = [0] * n
for i in range(1,n-1,1):
K1[i] = max(K1[i-1] + A[i], 0)
for i in range(n-2,0,-1):
K2[i] = max(K2[i+1]+A[i], 0)
maximum = 0;
for i in range(1,n-1,1):
maximum = max(maximum, K1[i-1]+K2[i+1])
return maximum
def main():
A = [3,2,6,-1,4,5,-1,2]
print(solution(A))
if __name__ == '__main__': main()
Ruby 100%
def solution(a)
max_starting =(a.length - 2).downto(0).each.inject([[],0]) do |(acc,max), i|
[acc, acc[i]= [0, a[i] + max].max ]
end.first
max_ending =1.upto(a.length - 3).each.inject([[],0]) do |(acc,max), i|
[acc, acc[i]= [0, a[i] + max].max ]
end.first
max_ending.each_with_index.inject(0) do |acc, (el,i)|
[acc, el.to_i + max_starting[i+2].to_i].max
end
end
Suppose I am given:
A range of integers iRange (i.e. from 1 up to iRange) and
A desired number of combinations
I want to find the number of all possible combinations and print out all these combinations.
For example:
Given: iRange = 5 and n = 3
Then the number of combinations is iRange! / ((iRange!-n!)*n!) = 5! / (5-3)! * 3! = 10 combinations, and the output is:
123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345
Another example:
Given: iRange = 4 and n = 2
Then the number of combinations is iRange! / ((iRange!-n!)*n!) = 4! / (4-2)! * 2! = 6 combinations, and the output is:
12 - 13 - 14 - 23 - 24 - 34
My attempt so far is:
#include <iostream>
using namespace std;
int iRange= 0;
int iN=0;
int fact(int n)
{
if ( n<1)
return 1;
else
return fact(n-1)*n;
}
void print_combinations(int n, int iMxM)
{
int iBigSetFact=fact(iMxM);
int iDiffFact=fact(iMxM-n);
int iSmallSetFact=fact(n);
int iNoTotComb = (iBigSetFact/(iDiffFact*iSmallSetFact));
cout<<"The number of possible combinations is: "<<iNoTotComb<<endl;
cout<<" and these combinations are the following: "<<endl;
int i, j, k;
for (i = 0; i < iMxM - 1; i++)
{
for (j = i + 1; j < iMxM ; j++)
{
//for (k = j + 1; k < iMxM; k++)
cout<<i+1<<j+1<<endl;
}
}
}
int main()
{
cout<<"Please give the range (max) within which the combinations are to be found: "<<endl;
cin>>iRange;
cout<<"Please give the desired number of combinations: "<<endl;
cin>>iN;
print_combinations(iN,iRange);
return 0;
}
My problem:
The part of my code related to the printing of the combinations works only for n = 2, iRange = 4 and I can't make it work in general, i.e., for any n and iRange.
Your solution will only ever work for n=2. Think about using an array (combs) with n ints, then the loop will tick up the last item in the array. When that item reaches max update then comb[n-2] item and set the last item to the previous value +1.
Basically working like a clock but you need logic to find what to uptick and what the next minimum value is.
Looks like a good problem for recursion.
Define a function f(prefix, iMin, iMax, n), that prints all combinations of n digits in the range [iMin, iMax] and returns the total number of combinations. For n = 1, it should print every digit from iMin to iMax and return iMax - iMin + 1.
For your iRange = 5 and n = 3 case, you call f("", 1, 5, 3). The output should be 123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345.
Notice that the first group of outputs are simply 1 prefixed onto the outputs of f("", 2, 5, 2), i.e. f("1", 2, 5, 2), followed by f("2", 3, 5, 2) and f("3", 4, 5, 2). See how you would do that with a loop. Between this, the case for n = 1 above, and traps for bad inputs (best if they print nothing and return 0, it should simplify your loop), you should be able to write f().
I'm stopping short because this looks like a homework assignment. Is this enough to get you started?
EDIT: Just for giggles, I wrote a Python version. Python has an easier time throwing around sets and lists of things and staying legible.
#!/usr/bin/env python
def Combos(items, n):
if n <= 0 or len(items) == 0:
return []
if n == 1:
return [[x] for x in items]
result = []
for k in range(len(items) - n + 1):
for s in Combos(items[k+1:], n - 1):
result.append([items[k]] + s)
return result
comb = Combos([str(x) for x in range(1, 6)], 3)
print len(comb), " - ".join(["".join(c) for c in comb])
Note that Combos() doesn't care about the types of the items in the items list.
Here is your code edited :D :D with a recursive solution:
#include <iostream>
int iRange=0;
int iN=0; //Number of items taken from iRange, for which u want to print out the combinations
int iTotalCombs=0;
int* pTheRange;
int* pTempRange;
int find_factorial(int n)
{
if ( n<1)
return 1;
else
return find_factorial(n-1)*n;
}
//--->Here is another solution:
void print_out_combinations(int *P, int K, int n_i)
{
if (K == 0)
{
for (int j =iN;j>0;j--)
std::cout<<P[j]<<" ";
std::cout<<std::endl;
}
else
for (int i = n_i; i < iRange; i++)
{
P[K] = pTheRange[i];
print_out_combinations(P, K-1, i+1);
}
}
//Here ends the solution...
int main()
{
std::cout<<"Give the set of items -iRange- = ";
std::cin>>iRange;
std::cout<<"Give the items # -iN- of iRange for which the combinations will be created = ";
std::cin>>iN;
pTheRange = new int[iRange];
for (int i = 0;i<iRange;i++)
{
pTheRange[i]=i+1;
}
pTempRange = new int[iN];
iTotalCombs = (find_factorial(iRange)/(find_factorial(iRange-iN)*find_factorial(iN)));
std::cout<<"The number of possible combinations is: "<<iTotalCombs<<std::endl;
std::cout<<"i.e.the combinations of "<<iN<<" elements drawn from a set of size "<<iRange<<" are: "<<std::endl;
print_out_combinations(pTempRange, iN, 0);
return 0;
}
Here's an example of a plain recursive solution. I believe there exists a more optimal implementation if you replace recursion with cycles. It could be your homework :)
#include <stdio.h>
const int iRange = 9;
const int n = 4;
// A more efficient way to calculate binomial coefficient, in my opinion
int Cnm(int n, int m)
{
int i;
int result = 1;
for (i = m + 1; i <= n; ++i)
result *= i;
for (i = n - m; i > 1; --i)
result /= i;
return result;
}
print_digits(int *digits)
{
int i;
for (i = 0; i < n; ++i) {
printf("%d", digits[i]);
}
printf("\n");
}
void plus_one(int *digits, int index)
{
int i;
// Increment current digit
++digits[index];
// If it is the leftmost digit, run to the right, setup all the others
if (index == 0) {
for (i = 1; i < n; ++i)
digits[i] = digits[i-1] + 1;
}
// step back by one digit recursively
else if (digits[index] > iRange) {
plus_one(digits, index - 1);
}
// otherwise run to the right, setting up other digits, and break the recursion once a digit exceeds iRange
else {
for (i = index + 1; i < n; ++i) {
digits[i] = digits[i-1] + 1;
if (digits[i] > iRange) {
plus_one(digits, i - 1);
break;
}
}
}
}
int main()
{
int i;
int digits[n];
for (i = 0; i < n; ++i) {
digits[i] = i + 1;
}
printf("%d\n\n", Cnm(iRange, n));
// *** This loop has been updated ***
while (digits[0] <= iRange - n + 1) {
print_digits(digits);
plus_one(digits, n - 1);
}
return 0;
}
This is my C++ function with different interface (based on sts::set) but performing the same task:
typedef std::set<int> NumbersSet;
typedef std::set<NumbersSet> CombinationsSet;
CombinationsSet MakeCombinations(const NumbersSet& numbers, int count)
{
CombinationsSet result;
if (!count) throw std::exception();
if (count == numbers.size())
{
result.insert(NumbersSet(numbers.begin(), numbers.end()));
return result;
}
// combinations with 1 element
if (!(count - 1) || (numbers.size() <= 1))
{
for (auto number = numbers.begin(); number != numbers.end(); ++number)
{
NumbersSet single_combination;
single_combination.insert(*number);
result.insert(single_combination);
}
return result;
}
// Combinations with (count - 1) without current number
int first_num = *numbers.begin();
NumbersSet truncated_numbers = numbers;
truncated_numbers.erase(first_num);
CombinationsSet subcombinations = MakeCombinations(truncated_numbers, count - 1);
for (auto subcombination = subcombinations.begin(); subcombination != subcombinations.end(); ++subcombination)
{
NumbersSet cmb = *subcombination;
// Add current number
cmb.insert(first_num);
result.insert(cmb);
}
// Combinations with (count) without current number
subcombinations = MakeCombinations(truncated_numbers, count);
result.insert(subcombinations.begin(), subcombinations.end());
return result;
}
I created a next_combination() function similar to next_permutation(), but valid input is required to make it work
//nums should always be in ascending order
vector <int> next_combination(vector<int>nums, int max){
int size = nums.size();
if(nums[size-1]+1<=max){
nums[size-1]++;
return nums;
}else{
if(nums[0] == max - (size -1)){
nums[0] = -1;
return nums;
}
int pos;
int negate = -1;
for(int i = size-2; i>=0; i--){
if(nums[i]+1 <= max + negate){
pos = i;
break;
}
negate --;
}
nums[pos]++;
pos++;
while(pos<size){
nums[pos] = nums[pos-1]+1;
pos++;
}
}
return nums;
}