Triangle: Determine if an array includes a triangular triplet (Codility) - c++

This is the Triangle problem from Codility:
A zero-indexed array A consisting of N integers is given.
A triplet (P, Q, R) is triangular if 0 ≤ P < Q < R < N and:
A[P] + A[Q] > A[R],
A[Q] + A[R] > A[P],
A[R] + A[P] > A[Q].
Write a function:
int solution(vector<int> &A);
that, given a zero-indexed array A consisting of N integers, returns 1
if there exists a triangular triplet for this array and returns 0
otherwise.
For example, given array A such that:
A[0] = 10, A[1] = 2, A[2] = 5, A[3] = 1, A[4] = 8, A[5] = 20
Triplet (0, 2, 4) is triangular, the function should return 1.
Given array A such that:
A[0] = 10, A[1] = 50, A[2] = 5, A[3] = 1
function should return 0.
Assume that:
N is an integer within the range [0..100,000];
each element of array A is an integer within the range
[−2,147,483,648..2,147,483,647].
And here is my solution in C++:
int solution(vector<int> &A) {
if(A.size()<3) return 0;
sort(A.begin(), A.end());
for(int i=0; i<A.size()-2; i++){
//if(A[i] = A[i+1] = A[i+2]) return 1;
if(A[i]+A[i+1]>A[i+2] && A[i+1]+A[i+2]>A[i] && A[i+2]+A[i]>A[i+1]){
return 1;
}
}
return 0;
}
I've checked the comments there and all the solutions seems similar to mine.
However, while others claimed to have gotten 100%, I only got a 93% score.
I got all the tests cases correct EXCEPT for one:
extreme_arith_overflow1
overflow test, 3 MAXINTs
I assume this case has some input like this:
[2147483647, 2147483647, 2147483647]
So I add this to the custom test case, and the answer turns out to be 0 when it clearly should be 1.
I also tried [1900000000, 1900000000, 1900000000], and the answer is still 0.
However, [1000000000, 1000000000, 1000000000] is correct with answer of 1.
Can anyone clue me in on why this result occured?
Greatly appreciated.

My solution in Java with 100/100 and time complexity of O(N*log(N))
With comments explaining the logic
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.Arrays;
class Solution {
public int solution(int[] A) {
int N = A.length;
if (N < 3) return 0;
Arrays.sort(A);
for (int i = 0; i < N - 2; i++) {
/**
* Since the array is sorted A[i + 2] is always greater or equal to previous values
* So A[i + 2] + A[i] > A[i + 1] ALWAYS
* As well ass A[i + 2] + A[i + 1] > A[i] ALWAYS
* Therefore no need to check those. We only need to check if A[i] + A[i + 1] > A[i + 2]?
* Since in case of A[i] + A[i + 1] > MAXINT the code would strike an overflow (ie the result will be greater than allowed integer limit)
* We'll modify the formula to an equivalent A[i] > A[i + 2] - A[i + 1]
* And inspect it there
*/
if (A[i] >= 0 && A[i] > A[i + 2] - A[i + 1]) {
return 1;
}
}
return 0;
}
Basically when you check X + Y value of integers, that is greater than integer limit the code will fail on overflow. so instead of checking if X + Y > Z, we can simply check the equivalent statement if X > Z - Y (simple math isn't it?). Alternatively you could always use long but it will be a worse solution memory wise.
Also make sure you skip the negatives as a triangle cannot have a negative side value.
Cheers

Java 100 %:
public int solution(int[] A){
Arrays.sort(A);
for(int i=0;i<A.length-2;i++){
if(
((long)A[i] + (long)A[i+1] > A[i+2]) &&
((long)A[i+1] + (long)A[i+2] > A[i]) &&
((long)A[i] + (long)A[i+2] > A[i+1])
)
return 1;
}
return 0;
}

Here's my clean solution in Python. I got a 100% in Codility.
This logic can be adapted to any other programming language.
Note: If the array is sorted, you only have to check that the sum of two consecutive elements is greater than the next element (A[i] + A[i+1] > A[i+2]), because in that case, the other two conditions (A[i+1]+A[i+2] > A[i], A[i]+A[i+2] > A[i+1]) will always be true.
I hope it helps.
def solution(A):
#edge case check
if len(A) < 3:
return 0
A.sort()
for i in range(len(A)-2):
if A[i]+A[i+1] > A[i+2]:
return 1
return 0

There are couple of issues here
Side of a triangle can't be 0, since it is a length. You have to add that check or you'll fail that corner case. i.e. Wouldn't get 100%.
Since you can have an input array of all INT_MAX or LONG_MAX (see http://www.cplusplus.com/reference/climits/), you need to store the sum in a double or long long.
You don't have to check all three conditions here i.e.
A[P] + A[Q] > A[R],
A[Q] + A[R] > A[P],
A[R] + A[P] > A[Q].
If you have sorted the array than
A[Q] + A[R] > A[P] &&
A[R] + A[P] > A[Q]
are always true because 0 ≤ P < Q < R i.e. R is greater than P and Q.
So you should only check for A[P] + A[Q] > A[R].
You have already placed a check for A.size() < 3 so that is good.
I have added a C implementation at https://github.com/naveedrasheed/Codility-Solutions/blob/master/Lesson6_Sorting/triangle.c.
You can compare it with solution.

I have used 3 for loop here( without sorting the array) to solve this problem.
public static int solution(int[] A) {
for (int p = 0; p < A.length; p++) {
for (int q = p + 1; q < A.length; q++) {
for (int r = q + 1; r < A.length; r++) {
if ((A[p] + A[q] > A[r]) && (A[q] + A[r] > A[p]) && (A[r] + A[p] > A[q])) {
System.out.println(A[p] + " " + A[q] + " " + A[r]);
return 1;
}
}
}
}
return 0;
}

the trick is to find a number on the array that is less the sum of the other two on the array so sorting the array then searching for that number will solve it. casting to long that on sometimes the value of summation wil exceed the allowed integer
public int solution(int[] A) {
int n = A.length;
if(n<3){
return 0;
}
Arrays.sort(A);
for(int i=2; i<n; i++){
if(A[i]<(long)A[i-1]+(long)A[i-2])
return 1;
}
return 0;
}

My solution in C# with 100 score.
using System;
class Solution {
public int solution(int[] A) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
if(A.Length) <3)
return 0;
Array.Sort(A);
int p,q,r;
for(int i=A.Length-1;i>1; i--){
p = A[i];
q = A[i-1];
r = A[i-2];
if(p+q>r && q+r > p && r+p > q)
return 1;
}
return 0;
}
}

Straightforward solution in JavaScript.
Note: I excluded the options where any side could be 0 or less. The rest is the same.
function solution(A) {
if (A.length < 3) return 0;
A.sort((a, b) => (a - b));
for (i = A.length - 1; i >= 0; i--) {
if (A[i - 2] <= 0) return 0;
if (
A[i] + A[i - 1] > A[i - 2] &&
A[i] + A[i - 2] > A[i - 1] &&
A[i - 1] + A[i - 2] > A[i]
) return 1;
}
return 0;
}

javascript 100% on codility
function solution(a) {
if (a.length < 3) {
return 0;
}
a.sort((a, b) => a - b);
for (let i = 0; i < a.length - 2; i++) {
if (a[i] + a[i + 1] > a[i + 2]) {
return 1;
}
}
return 0;
}

My solution to this problem, written in Swift.
public func Triangle(_ A : inout [Int]) -> Int {
A.sort()
for i in 1..<A.count-1 {
if(A[i] + A[i-1] > A[i+1]) {
print("Triangle has edges: \(A[i-1]), \(A[i]), \(A[i+1])")
return 1
}
}
return 0
}
A = [10,2,5,1,8,20]
print("Triangle: ", Triangle(&A))

Or you can change the if clause, like below
if(A[i]>A[i+2]-A[i+1] && A[i+1]>A[i]-A[i+2] && A[i+2]>A[i+1]-A[i])
using subtraction instead of addition.

Works 100%, tested with different scenario's.
I think all the possibilities are not covered above solution
Combination with
P,Q,R
A[0] = 10, A[1] = 2, A[2] = 5, A[3] = 1, A[4] = 8, A[5] = 20
index combination
0+1>2, 1+2>0, 2+0>1
1+2>3, 2+3>1, 3+1>2
....
These are combinations needed to achieve this problem.
//Triangle
/**
* A[P] + A[Q] > A[R],
A[Q] + A[R] > A[P],
A[R] + A[P] > A[Q]
*/
public int triangleSolution(int[] A) {
int status = 0;
for(int i=0; i<A.length; i++) {
int[] B = removeTheElement(A, i);
for(int j=0; j<B.length; j++) {
int[] C = removeTheElement(B, j);
for(int k=0; k<C.length; k++) {
if((A[i] + B[j] > C[k]) &&
(B[j] + C[k] > A[i]) &&
(C[k] + A[i] > B[j])) {
return 1;
}
}
}
}
return status;
}
// Function to remove the element
public int[] removeTheElement(int[] arr, int index)
{
// Create another array of size one less
int[] anotherArray = new int[arr.length - 1];
// Copy the elements except the index
// from original array to the other array
for (int i = 0, k = 0; i < arr.length; i++) {
// if the index is
// the removal element index
if (i == index) {
continue;
}
// if the index is not
// the removal element index
anotherArray[k++] = arr[i];
}
//Java >8
//IntStream.range(0, arr.length).filter(i -> i != index).map(i -> arr[i]).toArray();
return anotherArray;
}

//My solution in C++ it avoid overflow
inline int Triangle(vector<int> &A) {
if(A.size() < 3) return 0;
sort(A.begin(), A.end());
for(int i = 0; i < (int)A.size() - 2; ++i){
int P = A[i], Q = A[i + 1], R =A[i + 2];
if(( R - P - Q < 0) && ( P - Q - R < 0) && (Q - R - P < 0))
return 1;
}
return 0;
}

Ruby 100% solution
def solution(a)
arr = a.select{|x| x >=0 }.sort
arr.each_with_index do |p, pi|
arr[(pi+1)..-1].each_with_index do |q, qi|
arr[(qi+pi+2)..-1].each do |r|
break if p+q <=r
break if p+r <=q
break if r+q <=p
return 1
end
end
end
0
end

It's javascript solution(TC: O(N*log(N)) though, in case you guys want :).
function solution(A) {
if(A.length<3) return 0;
A.sort((a,b)=>b - a);
for(let i = 0,j = i+1;j < A.length-1;j++){
let p = A[j],q = A[j+1],r = A[i]
if(r - p > q) i++;
else if(r - p < q) return 1;
}
return 0;
}

Sorting does not work now, It was a bug it was fixed by Codility. Now, I am using this piece of code to get 93%
You can see the results below:
Codility test Results
0 <= P < Q < R < N
public static int solution(int[] unfilteredArray) {
int[] array = filterLessThanOneElements(unfilteredArray);
for(int i = 0; i <= (array.length - 3) ; i++) {
long p = array[i];
for(int j = i+1; j <= (array.length - 2); j++) {
long q = array[j];
for(int k = j+1; k <= (array.length - 1); k++) {
long r = array[k];
if((p + q > r) && (q + r > p) && (r + p > q)) {
return 1;
}
}
}
}
return 0;
}
// The mose efficient way to remove duplicates
// TIME COMPLEXITY : O(N)
private static int[] filterLessThanOneElements(int[] unfilteredArray) {
int k = 0;
for(int i = 0; i < unfilteredArray.length; i++) {
if(unfilteredArray[i] > 0) {
unfilteredArray[k++] = unfilteredArray[i];
}
}
return Arrays.copyOfRange(unfilteredArray, 0, k);
}

Simple change: First, you observe that negative integers cannot be part of a triangular triplet. That means you can cast all ints to unsigned int, and there can’t be any overflow anymore.

100/100 JavaScript solution
function solution(A) {
let l = A.length;
if (l < 3) {
return 0;
}
A.sort((a, b) => a - b);
for (let i = 0; i < l - 2; i++) {
let [p, q, r] = [A[i], A[i + 1], A[i + 2]];
if (p + q > r && q + r > p && r + p > q) {
return 1;
}
}
return 0;
}

If you don't want to use Array.sort, the following works with 100% correctness and 100% performance with a complexity that codility detects at O(N*log(N)).
class Solution {
public int solution(int[] A) {
int ans = 0;
int p1 = -1;
int p2 = -1;
int p1Pos = 0;
int p2Pos = 1;
int cur = -1;
if(A.length > 2){
p1 = A[0];
p2 = A[1];
for(int i = p2Pos + 1; i < A.length; i++){
if(p1 > p2){
p2 = A[p1Pos];
p1 = A[p2Pos];
A[p2Pos] = p2;
A[p1Pos] = p1;
}
cur = A[i];
//System.out.println(p1 + " " + p2 + " " + cur);
if(p1 > -1 && p2 > -1){
//the test for a triangle
if (cur > -1 &&
((p1 == p2 && p2 == cur) ||
((p1 + p2 > cur) && (p1 + cur > p2) && ( cur + p2 > p1)))){
return 1;
//bubble sort...sort of
}else if (p2 > cur){
A[p2Pos] = cur;
A[i] = p2;
if(p1 < cur){
p1 = cur;
p1Pos = p2Pos;
}
p2Pos = i;
} else if(cur > -1
&&(p1 + p2 <= cur) ){
p2Pos++;
p1Pos++;
p1=p2;
p2=cur;
}
}else{
//find the first two positive numbers
if((p2 < 0 || p1 < 0) && cur > -1){
if(p1 < 0){
p1 = cur;
p1Pos = i;
}else{
p2 = A[i];
p2Pos = i;
}
}
}
}
}
return ans;
}
}
When I was making this I thought that maybe I could solve this while doing a modified Bubble sort. I chose two pivots (p1 and p2), while making sure p2 > p1.
As I iterated through the array, I made sure that p2 would bubble up if p2 > cur and that p1 would additionally bubble up if p1 > cur. I Furthermore, I noticed that any combination of three points that have a negative number cannot be a triangle. So I ignored negatives. I also realized that if the array happened to hold three and only maximal integers that I would have an issue. To solve this I tested for p1 == p2 == cur. Admittingly, it might be better to use BigInteger to solve it.

My 100% JavaScript solution with O(N*log(N)) time complexity:
function solution(A) {
A.sort((a, b) => a - b);
for (let i = 0, len = A.length - 2; i < len; i++) {
const [P, Q, R] = [A[i], A[i + 1], A[i + 2]];
if (P + Q > R && Q + R > P && R + P > Q) {
return 1;
}
}
return 0;
}

One would think that sorting the array first will violate the condition 0<=P<Q<R. But the question is does such a triple exist. For the example we find [10,2,5,1,8,20]. After sorting we still find the values 10, 5, and 8 as the triple, but in a different order.
A Python 3 solution with 100% score at Codility:
def triangle(A):
n = len(A)
if n < 3:
return 0
a = list(A)
if 0 not in a:
a.append(0)
a.sort()
#print(a)
n = len(a)
p_a = a[a.index(0)+1:n]
#print(p_a)
n = len(p_a)
for i in range(n-2):
p = p_a[i]
q = p_a[i+1]
r = p_a[i+2]
if (p+q>r):
return (1)
return 0

Better solutions for C++ is to change a little algorithm. Make subtraction instead of adding, here is an example:
int solution(vector<int> &A) {
if (A.size() < 3)
return 0;
sort(A.begin(), A.end());
for (int i = 0; i < A.size() - 2; i++) {
if (A[i] > 0 && (A[i] > A[i + 2] - A[i + 1]))
return 1;
}
return 0;
}

It's because of integer overflow.
Try out this one:
int a1 = 1900000000;
int a2 = 1900000000;
int sum = a1+a2; // sum will be -494967296
Edit: Use long long int.
long long int sum01 = A[i] + A[i+1];
long long int sum12 = A[i+1] + A[i+2];
long lont int sum02 = A[i] + A[i+2];
if (sum01 > A[i+2] && sum12 > A[i] && sum02 > A[i+1])
return 1;

My java Solution 100/100 Instead of comparing the Addition we compare the subtraction as we can have an Integer.MAX_VALUE an we will be getting corrupted data.
public static int solution(int[] A) {
int isATriangle = 0;
Arrays.sort(A);
if (A.length >= 3) {
for (int i = 0; i < A.length - 2; i++) {
if (A[i] > A[i + 2] - A[i + 1]
&& A[i + 2] > A[i] - A[i + 1]
&& A[i + 2] > A[i + 1] - A[i])
isATriangle = 1;
}
}
return isATriangle;
}

Related

FLUTTER - Problem with List constructor and null safety mode

I would like to use this "Levenshtein" function to assess similarities between two strings (to check if user has committed a spelling mistake).
Since I work on null safe mode, it points out an error with the LIST constructor :
List<List<int>> d = List.generate(sa + 1, (int i) => List(sb + 1));
What can I write to replace List(sb+1)); ?
int levenshtein(String a, String b) {
a = a.toUpperCase();
b = b.toUpperCase();
int sa = a.length;
int sb = b.length;
int i, j, cost, min1, min2, min3;
int levenshtein;
// ignore: deprecated_member_use
List<List<int>> d = List.generate(sa + 1, (int i) => List(sb + 1));
if (a.length == 0) {
levenshtein = b.length;
return (levenshtein);
}
if (b.length == 0) {
levenshtein = a.length;
return (levenshtein);
}
for (i = 0; i <= sa; i++) d[i][0] = i;
for (j = 0; j <= sb; j++) d[0][j] = j;
for (i = 1; i <= a.length; i++)
for (j = 1; j <= b.length; j++) {
if (a[i - 1] == b[j - 1])
cost = 0;
else
cost = 1;
min1 = (d[i - 1][j] + 1);
min2 = (d[i][j - 1] + 1);
min3 = (d[i - 1][j - 1] + cost);
d[i][j] = min(min1, min(min2, min3));
}
levenshtein = d[a.length][b.length];
return (levenshtein);
}
You can use List.generate for the inner list as well.
List<List<int>> d = List.generate(sa + 1, (int i) => List.generate(sb + 1, (int j) => 0));
Also, if they're all going to be initialized to 0 you can just do this too:
List<List<int>> d = List.filled(sa + 1, List.filled(sb + 1, 0));

Problem when inserting in a Sparse Matrix

I have to implement the CSR matrix data structure in C++ using 3 dynamic arrays (indexing starts at 0) and I've got stuck. So I have to implement 2 functions:
1) modify(int i, int j, TElem e) - modifies the value of (i,j) to e or adds if (if it does not exist) or deletes it if e is null.
2) element(int i, int j) const - returns the value found on (i,j)
I wanted to test my code in the next way:
Matrix m(10, 10);
for (int j = 0; j < m.nrColumns(); j++) {
m.modify(4, j, 3);
}
for (int i = 0; i < m.nrLines(); i++)
for (int j = 0; j < m.nrColumns(); j++)
if (i == 4)
assert(m.element(i, j) == 3);
else
assert(m.element(i, j) == NULL_TELEM);
And I got a surprise to see that m.element(4,j) will be 0 for j in the range (0,8) and only 3 for j=9.
This is my implementation of element(int i, int j) :
int currCol;
for (int pos = this->lines[i]; pos < this->lines[i+1]; pos++) {
currCol = this->columns[pos];
if (currCol == j)
return this->values[pos];
else if (currCol > j)
break;
}
return NULL_TELEM;
The constructor looks like this:
Matrix::Matrix(int nrLines, int nrCols) {
if (nrLines <= 0 || nrCols <= 0)
throw exception();
this->nr_lines = nrLines;
this->nr_columns = nrCols;
this->values = new TElem[1000];
this->values_capacity = 1;
this->values_size = 0;
this->lines = new int[nrLines + 1];
this->columns = new TElem[1000];
this->columns_capacity = 1;
this->columns_size = 0;
for (int i = 0; i <= nrLines; i++)
this->lines[i] = NULL_TELEM;
}
This is the "modify" method:
TElem Matrix::modify(int i, int j, TElem e) {
if (i < 0 || j < 0 || i >= this->nr_lines || j >= nr_columns)
throw exception();
int pos = this->lines[i];
int currCol = 0;
for (; pos < this->lines[i + 1]; i++) {
currCol = this->columns[pos];
if (currCol >= j)
break;
}
if (currCol != j) {
if (!(e == 0))
add(pos, i, j, e);
}
else if (e == 0)
remove(pos, i);
else
this->values[pos] = e;
return NULL_TELEM;
}
And this is the inserting method:
void Matrix::add(int index, int line, int column, TElem value)
{
this->columns_size++;
this->values_size++;
for (int i = this->columns_size; i >= index + 1; i--) {
this->columns[i] = this->columns[i - 1];
this->values[i] = this->values[i - 1];
}
this->columns[index] = column;
this->values[index] = value;
for (int i = line + 1; i <= this->nr_lines; i++)
this->lines[i]++;
}
Can somebody help me, please? I can't figure out why this happens and I really need to finish this implementation these days. It's pretty weird that is sees those positions having the value 0.
So having the next test that starts in the next way, I get a memory acces violation:
Matrix m(200, 300);
for (int i = m.nrLines() / 2; i < m.nrLines(); i++) {
for (int j = 0; j <= m.nrColumns() / 2; j++)
{
int v1 = j;
int v2 = m.nrColumns() - v1 - 1;
if (i % 2 == 0 && v1 % 2 == 0)
m.modify(i, v1, i * v1);
else
if (v1 % 3 == 0)
m.modify(i, v1, i + v1);
if (i % 2 == 0 && v2 % 2 == 0)
m.modify(i, v2, i * v2);
else
if (v2 % 3 == 0)
m.modify(i, v2, i + v2);
}
The error is thrown in the method "modify" at currCol = this->column[pos];
And if I look into the debugger it looks like:i=168, lines[i]=-842150451, lines[i+1]=10180,pos=-842150451.
Does anybody have any ideas why it looks this way?
Your code has two small errors.
When you try to find the insertion position in modify, you loop over the non-empty elements in the row:
int currCol = 0;
for (; pos < this->lines[i + 1]; i++) {
currCol = this->columns[pos];
if (currCol >= j)
break;
}
Here, you must update pos++ in each iteration instead of i++.
The second error occurs when you insert an element into column 0. The currCol will be zero, but your condition for adding a new element is
if (currCol != j) {
if (!(e == 0))
add(pos, i, j, e);
}
But j is zero, too, so nothing will be inserted. You can fix this by starting with a non-existing column:
int currCol = -1;

Memoized solution gives TLE while tabulated solution does not

I am attempting the following question from Interviewbit:
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
NOTE: You can only move either down or right at any point in time.
I have written the following memoized solution:
int minPath(vector<vector<int> > &A, int i, int j, vector<vector<int> > &dp) {
if (dp[i][j] >= 0)
return dp[i][j];
else if (i == A.size() - 1 && j == A[0].size() - 1)
return dp[i][j] = A[i][j];
else if (i == A.size() - 1)
return dp[i][j] = A[i][j] + minPath(A, i, j + 1, dp);
else if (j == A[0].size() - 1)
return dp[i][j] = A[i][j] + minPath(A, i + 1, j, dp);
else
return dp[i][j] = A[i][j] + min(minPath(A, i + 1, j, dp), minPath(A, i, j + 1, dp));
}
int Solution::minPathSum(vector<vector<int> > &A) {
if (A.size() == 0)
return 0;
vector<vector<int> > dp(A.size(), vector<int>(A[0].size(), -1));
return minPath(A, 0, 0, dp);
}
This solution is giving a TLE during submission.
After a while I took a look at the editorial code, and they have followed the tabulation approach as follows:
int minPathSum(vector<vector<int> > &grid) {
if (grid.size() == 0) return 0;
int m = grid.size(), n = grid[0].size();
int minPath[m + 1][n + 1];
for (int i = 0; i < m; i++) {
minPath[i][0] = grid[i][0];
if (i > 0) minPath[i][0] += minPath[i - 1][0];
for (int j = 1; j < n; j++) {
minPath[i][j] = grid[i][j] + minPath[i][j-1];
if (i > 0) minPath[i][j] = min(minPath[i][j], grid[i][j] + minPath[i-1][j]);
}
}
return minPath[m-1][n-1];
}
According to me, the time complexity of both the codes seem same, yet mine seems to be giving TLE. Where exactly am I going wrong?
The test cases have negative numbers in the grid ( though they have explicitly mentioned non-negative numbers). So dp[i][j] can be negative but your function will never consider those values. Just used another vector to store the visited cell and it got accepted.
int minPath(vector<vector<int> > &A, int i, int j, vector<vector<int> > &dp,vector<vector<bool> > &vis)
{
if (vis[i][j])
return dp[i][j];
vis[i][j] = 1;
if (i == A.size() - 1 && j == A[0].size() - 1)
return dp[i][j] = A[i][j];
else if (i == A.size() - 1)
return dp[i][j] = A[i][j] + minPath(A, i, j + 1, dp, vis);
else if (j == A[0].size() - 1)
return dp[i][j] = A[i][j] + minPath(A, i + 1, j, dp, vis);
else
return dp[i][j] = A[i][j] + min(minPath(A, i + 1, j, dp, vis), minPath(A, i, j + 1, dp, vis));
}
int Solution::minPathSum(vector<vector<int> > &A)
{
if (A.size() == 0)
return 0;
vector<vector<int> > dp(A.size(), vector<int>(A[0].size(), -1));
vector<vector<bool> > vis(A.size(), vector<bool>(A[0].size(), 0));
return minPath(A, 0, 0, dp, vis);
}

Codility MinAbsSum

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
}

Why is this Segfault Occuring?

So I've traced the segfault to the line, but I don't understand why this is a segfault. Can someone elaborate on the error of my ways?
Here are the variable declarations.
size_t i, j, n, m, chunk_size, pixel_size;
i = j = n = m = 0;
chunk_size = 256;
pixel_size = 4;
Here are the array declarations.
uint8_t** values = new uint8_t*[chunk_size];
for (i = 0; i < chunk_size; ++ i)
values[i] = new uint8_t[chunk_size];
float** a1 = new float*[chunk_size];
for (i = 0; i < chunk_size; ++i)
a1[i] = new float[chunk_size];
And here is where the segfault occurs.
float delta, d;
for (i = 0; i < 256; ++i) {
for (j = n = m = d = 0; j < 256; j = m) {
while (i == 0 || d != 0) {
d = a1[i][m]; <------SEGFAULT per GDB
++m;
}
delta = (d - a1[i][j]) / m;
n = j + 1;
while (n < j + m) {
a1[i][n] = a1[i][n - 1] + delta;
++n;
}
}
}
I'm fairly new to C++ and can't figure out why this would be a segfault. Is this not the proper way to set a variables value to a variable in an array? Is that the source of my segfault?
Note: The point of this whole thing is too expand a 4x4 array to a 256x256 array with my simpleton interpolation formula.
while (i == 0 || d != 0) {
d = a1[i][m]; <------SEGFAULT per GDB
++m;
}
This is an endless while loop, in some cases (e.g. in the first iteration of the outer loop).
Your outer loop starts out with i = 0 and the inner loops starts with d = 0 and the logic controlling the while loop is not sufficient (see code comment).
for (i = 0; i < 256; ++i) {
for (j = n = m = d = 0; j < 256; j = m) {
// Here i == 0 is ALWAYS true (so d != 0 is ignored due to
// short-circuit evaluation) and then 'm' is continuously incremented
// until it goes out of bounds
while (i == 0 || d != 0) {
d = a1[i][m]; <------SEGFAULT per GDB
++m;
}
delta = (d - a1[i][j]) / m;
n = j + 1;
while (n < j + m) {
a1[i][n] = a1[i][n - 1] + delta;
++n;
}
}
The problem is in the following lines :
while (i == 0 || d != 0) {
d = a1[i][m]; <------SEGFAULT per GDB
++m;
}
Your while loop will keep on going while i equals 0. Since you never increment i in your while loop, m keeps on incrementing forever until arriving out of bounds, causing the segfault issue that you are having.
Make sure you check the values of i and m, so that they are in the allocated memory range and your code will work.