├── Xi 2012.cs ├── Delta 2011.cs ├── Gamma 2011 O(N).cs ├── LICENSE.md ├── Alpha 2010.cs ├── Gamma 2011 O(N^2).cs ├── Pi 2012.cs ├── Kappa 2011.cs ├── Chi 2012.cs ├── Zeta 2011 O((N+M)K).cs ├── Sigma 2012.cs ├── Nu 2011 O(KNlog(N)).cs ├── Rho 2012.cs ├── Mu 2011.cs ├── Upsilon 2012.cs ├── Beta 2010.cs ├── ModularArithmetic.cs ├── Omicron 2012.cs ├── Lambda 2011.cs ├── Zeta 2011 O(NM).cs ├── Phi 2012.cs ├── Eta 2011.cs ├── README.md ├── Iota 2011.cs ├── Nu 2011 O(K(log(K)+log(N)+log(M))).cs ├── Theta 2011.cs ├── Tau 2012.cs └── Epsilon 2011.cs /Xi 2012.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpeczek/Codility/HEAD/Xi 2012.cs -------------------------------------------------------------------------------- /Delta 2011.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpeczek/Codility/HEAD/Delta 2011.cs -------------------------------------------------------------------------------- /Gamma 2011 O(N).cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpeczek/Codility/HEAD/Gamma 2011 O(N).cs -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 - 2020 Tomasz Pęczek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Alpha 2010.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Alpha 2010 4 | class Solution { 5 | public int solution(int[] A) { 6 | //This is where result will be stored 7 | int firstCoveringPrefix = 0; 8 | 9 | //Sanity check 10 | if (A != null && A.Length > 0) 11 | { 12 | //Because each element of array A is an integer within the range [0..A.Length-1] we can track the occurences in bool array of a same length 13 | //Important: Default value of bool in .Net is false 14 | bool[] occurences = new bool[A.Length]; 15 | 16 | //For every element in A array 17 | for (int i = 0; i < A.Length; i++) 18 | { 19 | //If the element haven't occured yet 20 | if (!occurences[A[i]]) 21 | { 22 | //Mark its occurence in tracking array 23 | occurences[A[i]] = true; 24 | //Change the covering prefix to current index as this is first occurance of this element 25 | firstCoveringPrefix = i; 26 | } 27 | } 28 | } 29 | 30 | //Return the result 31 | return firstCoveringPrefix; 32 | } 33 | } -------------------------------------------------------------------------------- /Gamma 2011 O(N^2).cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Gamma 2011 4 | class Solution { 5 | public int solution(string S) { 6 | //This is where result will be stored 7 | int palindromicSlicesCount = 0; 8 | 9 | //Sanity check 10 | if (!String.IsNullOrEmpty(S)) 11 | { 12 | //In order to be able to handle palindromes of odd and even lengths in the same way, we insert special character (#) between letters. 13 | //i.g. 'baababa' becomes 'b#a#a#b#a#b#a' 14 | S = String.Join("#", Array.ConvertAll(S.ToCharArray(), c => c.ToString())); 15 | int N = S.Length; 16 | 17 | //For every potential palindorme center 18 | for (int i = 1; i < N - 1; i++) 19 | { 20 | //Expand the palindrome starting by first no '#' character and steping by 2 21 | for (int j = 2 - i%2; i - j >= 0 && i + j < N; j = j + 2) 22 | { 23 | //If characters are the same 24 | if (S[i - j] == S[i + j]) 25 | { 26 | //Increase the number of palindromic slices (if it is above 100,000,000 we should return -1) 27 | if (++palindromicSlicesCount > 100000000) 28 | return -1; 29 | } 30 | else 31 | break; 32 | } 33 | } 34 | } 35 | 36 | //Return the result 37 | return palindromicSlicesCount; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pi 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Pi 2012 (based on Codility Blog --> http://blog.codility.com/2012/04/pi-2012-codility-programming.html) 5 | class Solution 6 | { 7 | #region Methods 8 | private int[] FindLeftClosestAscendersDistances(int[] A) 9 | { 10 | int N = A.Length; 11 | int[] leftClosestAscendersDistances = new int[N]; 12 | 13 | Stack stack = new Stack(N); 14 | for (int i = 0; i < N; i++) 15 | { 16 | while (stack.Count > 0 && A[stack.Peek()] <= A[i]) 17 | stack.Pop(); 18 | 19 | if (stack.Count == 0) 20 | leftClosestAscendersDistances[i] = Int32.MaxValue; 21 | else 22 | leftClosestAscendersDistances[i] = i - stack.Peek(); 23 | 24 | stack.Push(i); 25 | } 26 | 27 | return leftClosestAscendersDistances; 28 | } 29 | #endregion 30 | 31 | public int[] solution(int[] A) 32 | { 33 | //This is where result will be stored 34 | int[] closestAscendersDistances = null; 35 | 36 | //Sanity check 37 | if (A != null) 38 | { 39 | int N = A.Length; 40 | 41 | int[] leftClosestAscendersDistances = FindLeftClosestAscendersDistances(A); 42 | 43 | Array.Reverse(A); 44 | int[] rightClosestAscendersDistances = FindLeftClosestAscendersDistances(A); 45 | 46 | closestAscendersDistances = new int[N]; 47 | for (int i = 0; i < N; i++) 48 | { 49 | closestAscendersDistances[i] = leftClosestAscendersDistances[i] < rightClosestAscendersDistances[N - i - 1] ? leftClosestAscendersDistances[i] : rightClosestAscendersDistances[N - i - 1]; 50 | 51 | if (closestAscendersDistances[i] == Int32.MaxValue) 52 | closestAscendersDistances[i] = 0; 53 | } 54 | } 55 | 56 | //Return the result 57 | return closestAscendersDistances; 58 | } 59 | } -------------------------------------------------------------------------------- /Kappa 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | class Solution 5 | { 6 | #region Fields 7 | private ModularArithmetic _modularArithmetic = null; 8 | private List _factorials = new List() { 1 }; 9 | #endregion 10 | 11 | #region Constructor 12 | public Solution() 13 | { 14 | //Because potential numbers might be very big (worst case 1000000!) we will use modular arithemtic 15 | _modularArithmetic = new ModularArithmetic(1410000017); 16 | } 17 | #endregion 18 | 19 | #region Arithmetic 20 | //Factorial --> n! 21 | private long Factorial(int n) 22 | { 23 | int k = _factorials.Count - 1; 24 | //Store all the factorials up to the highest required 25 | while (k < n) 26 | _factorials.Add(_modularArithmetic.Multiply(_factorials[k++], k)); 27 | //This way every factorial is calculated only once 28 | return _factorials[n]; 29 | } 30 | 31 | //Binomial coefficient --> n!/(k!(n - k)!) 32 | private long BinomialCoefficient(int n, int k) 33 | { 34 | long numerator = Factorial(n); 35 | long denominator = _modularArithmetic.Multiply(Factorial(k), Factorial(n - k)); 36 | 37 | return _modularArithmetic.Divide(numerator, denominator); ; 38 | } 39 | #endregion 40 | 41 | public int solution(int[] T, int[] D) 42 | { 43 | //This is where result will be stored 44 | long numberOfCombinations = 0; 45 | 46 | //Sanity check 47 | if (T != null && T.Length > 0 && D != null && D.Length == D.Length) 48 | { 49 | int N = T.Length; 50 | numberOfCombinations = 1; 51 | 52 | //The general algorithm is very simple 53 | for (int i = 0; i < N; i++) 54 | //We mutltiply all the binominal coefficients to get the result (thanks to modular arithemtic the result is alread a proper remainder) 55 | numberOfCombinations = _modularArithmetic.Multiply(numberOfCombinations, BinomialCoefficient(T[i], D[i])); 56 | } 57 | 58 | //Return the result 59 | return (int)numberOfCombinations; 60 | } 61 | } -------------------------------------------------------------------------------- /Chi 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Chi 2012 5 | class Solution 6 | { 7 | public int[] solution(int[] A, int[] B) 8 | { 9 | //Sanity check 10 | if (A == null || A.Length > 30000) 11 | throw new ArgumentException("An array no bigger than 30,000 elements must be given", "A"); 12 | 13 | //Sanity check 14 | if (B == null || B.Length > 30000) 15 | throw new ArgumentException("An array no bigger than 30,000 elements must be given", "B"); 16 | 17 | int M = A.Length; 18 | int N = B.Length; 19 | 20 | //We will find the highest point in the landscape 21 | int highestLandscapePoint = 0; 22 | for (int i = 1; i < M; i++) 23 | { 24 | if (A[i] > A[highestLandscapePoint]) 25 | highestLandscapePoint = i; 26 | } 27 | 28 | //We will pre-calculate the positions at with the cannonballs will rest if fired at given height 29 | Dictionary possibleLandscapePoints = new Dictionary(); 30 | for (int i = 0; i <= highestLandscapePoint; i++) 31 | { 32 | for (int j = possibleLandscapePoints.Count; j <= A[i]; j++) 33 | possibleLandscapePoints.Add(j, i - 1); 34 | } 35 | 36 | //For every cannonball shot 37 | for (int i = 0; i < N; i++) 38 | { 39 | //If the shot will have impact on the landscape 40 | if (B[i] > A[0] && B[i] <= A[highestLandscapePoint]) 41 | { 42 | //Check at which position the cannonball will rest 43 | int landscapePoint = possibleLandscapePoints[B[i]]; 44 | 45 | //Adjust the height of landscape at this position 46 | A[landscapePoint]++; 47 | 48 | //If the cannonball fired at the new height of this position could rest here 49 | if (possibleLandscapePoints[A[landscapePoint]] > landscapePoint - 1) 50 | //Set up new rest position for cannonball fired at this new height 51 | possibleLandscapePoints[A[landscapePoint]] = landscapePoint - 1; 52 | } 53 | } 54 | 55 | //Return the result 56 | return A; 57 | } 58 | } -------------------------------------------------------------------------------- /Zeta 2011 O((N+M)K).cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Zeta 2011 4 | class Solution 5 | { 6 | //Directions definitions 7 | private const int _bottomDirection = -1; 8 | private const int _rightDirection = 1; 9 | private const int _neutralDirection = 0; 10 | 11 | public int solution(int[][] A, int K) 12 | { 13 | //This is where result will be stored 14 | int result = 0; 15 | 16 | //Sanity check 17 | if (A != null && A.Length > 0 && K > 0) 18 | { 19 | int N = A.Length; 20 | int M = A[0].Length; 21 | 22 | //For every ball 23 | for (int i = 1; i <= K; i++) 24 | { 25 | //We start from left top corner of the board 26 | int switchRowIndex = 0; 27 | int switchColumnIndex = 0; 28 | 29 | //With ball rolling to the bottom 30 | int ballDirection = _bottomDirection; 31 | 32 | //Until the ball will not exit the board 33 | while (switchRowIndex < N && switchColumnIndex < M) 34 | { 35 | int switchDirection = A[switchRowIndex][switchColumnIndex]; 36 | //If the current ball switch doesn't have neutral direction 37 | if (switchDirection != _neutralDirection) 38 | { 39 | //We change the direction of the ball based on switch value 40 | ballDirection = switchDirection; 41 | //And negate this value 42 | A[switchRowIndex][switchColumnIndex] = (-1) * switchDirection; 43 | } 44 | 45 | //Based on the direction of the ball we move it to the next switch 46 | if (ballDirection == _bottomDirection) 47 | switchRowIndex++; 48 | else 49 | switchColumnIndex++; 50 | } 51 | 52 | //If the ball has exited the board through bottom edge of the bottom-right switch 53 | if (switchRowIndex == N && switchColumnIndex == M - 1) 54 | //We increase the result 55 | result++; 56 | } 57 | } 58 | 59 | return result; 60 | } 61 | } -------------------------------------------------------------------------------- /Sigma 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Sigma 2012 5 | class Solution 6 | { 7 | public int solution(int[] H) 8 | { 9 | //This is where result will be stored 10 | int minimumNumberOfBlocks = 0; 11 | 12 | //Sanity check 13 | if (H != null && H.Length > 0 && H.Length <= 100000) 14 | { 15 | int N = H.Length; 16 | 17 | //We will truck the rectangles we currently have on stack 18 | Stack currentBlocks = new Stack(N); 19 | //And the height of the wall 20 | int currentHeight = 0; 21 | 22 | //Going through entire wall length 23 | for (int i = 0; i < N; i++) 24 | { 25 | //Until we will not achieve the desired wall height 26 | while (currentHeight != H[i]) 27 | { 28 | //If the current height is 0 29 | if (currentHeight == 0) 30 | { 31 | //We push one block with desired height 32 | currentBlocks.Push(H[i]); 33 | currentHeight = H[i]; 34 | minimumNumberOfBlocks++; 35 | } 36 | //If current height is less than desired 37 | else if (currentHeight < H[i]) 38 | { 39 | //We push one block with missing height 40 | currentBlocks.Push(H[i] - currentHeight); 41 | currentHeight = H[i]; 42 | minimumNumberOfBlocks++; 43 | } 44 | //If current height is greater than desired 45 | else 46 | { 47 | //We will be removing block until we will get height lower or equal to desired 48 | int topBlock = currentBlocks.Pop(); 49 | currentHeight = currentHeight - topBlock; 50 | } 51 | } 52 | } 53 | } 54 | else 55 | throw new ArgumentException("A non-empty array no bigger than 100,000 elements must be given", "H"); 56 | 57 | //Return the result 58 | return minimumNumberOfBlocks; 59 | } 60 | } -------------------------------------------------------------------------------- /Nu 2011 O(KNlog(N)).cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Nu 2011 4 | class Solution 5 | { 6 | public int solution(int[] A, int[] B, int[] P, int[] Q, int[] R, int[] S) 7 | { 8 | //This is where result will be stored 9 | int sequencesMediansMedian = 0; 10 | 11 | //Sanity check 12 | if (A != null && A.Length > 0 && B != null && B.Length > 0 && P != null && Q != null && R != null && S != null && P.Length == Q.Length && P.Length == R.Length && P.Length == S.Length && P.Length != 0) 13 | { 14 | int N = A.Length; 15 | int M = B.Length; 16 | int K = P.Length; 17 | 18 | int[] sequencesMedians = new int[K]; 19 | for (int I = 0; I < K; I++) 20 | { 21 | //We will be merging the sequence by adjusting indexes 22 | int leftParentIndex = P[I]; 23 | int rightParentIndex = R[I]; 24 | 25 | //We pre calculate the length of the target sequence 26 | int sequenceLength = (Q[I] - P[I] + 1) + (S[I] - R[I] + 1); 27 | //Because the task is dealing only with sequences of odd length, we know at which index the median will be 28 | int sequenceMedianIndex = sequenceLength / 2; 29 | 30 | //We go only through first half of the new sequence 31 | for (int sequenceIndex = 0; sequenceIndex <= sequenceMedianIndex; sequenceIndex++) 32 | { 33 | //We take element from either A or B depending on their values (this way we can keep the target sequence sorted) 34 | if (leftParentIndex > Q[I] || (rightParentIndex <= S[I] && B[rightParentIndex] < A[leftParentIndex])) 35 | { 36 | sequencesMedians[I] = B[rightParentIndex]; 37 | rightParentIndex++; 38 | } 39 | else 40 | { 41 | sequencesMedians[I] = A[leftParentIndex]; 42 | leftParentIndex++; 43 | } 44 | } 45 | } 46 | 47 | //We sort the sequence created from medians 48 | Array.Sort(sequencesMedians); 49 | //And get our result 50 | sequencesMediansMedian = sequencesMedians[K / 2]; 51 | } 52 | 53 | //Return the result 54 | return sequencesMediansMedian; 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Rho 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Rho 2012 (based on Codility Blog --> http://blog.codility.com/2012/05/rho-2012-codility-programming.html) 4 | class Solution 5 | { 6 | #region Methods 7 | //Method looking for next possible value in the sequence 8 | private bool SearchDivision(int[] sequence, int index, int A) 9 | { 10 | if (sequence[index] == A) 11 | return true; 12 | else if (index + 1 >= sequence.Length) 13 | return false; 14 | else if (!CanReach(sequence.Length - index - 1, sequence[index], A)) 15 | return false; 16 | 17 | for (int i = index; i > -1; i--) 18 | { 19 | for (int j = i; j > -1; j--) 20 | { 21 | if ((sequence[i] * 2 < sequence[index]) || (sequence[i] + sequence[j] <= sequence[index])) 22 | break; 23 | 24 | if (sequence[i] + sequence[j] <= A) 25 | { 26 | sequence[index + 1] = sequence[i] + sequence[j]; 27 | if (SearchDivision(sequence, index + 1, A)) 28 | return true; 29 | } 30 | } 31 | } 32 | 33 | return false; 34 | } 35 | 36 | //Method which checks if we can go from start value to target value within given number of steps assuming that we are doubling value on every step. 37 | private bool CanReach(int steps, int start, int target) 38 | { 39 | for (int i = 0; i < steps; i++) 40 | start *= 2; 41 | 42 | return start >= target; 43 | } 44 | #endregion 45 | 46 | public int[] solution(int A) 47 | { 48 | //This is where result will be stored 49 | int[] shortestSequence = null; 50 | 51 | //Sanity check 52 | if (A > 0 && A <= 600) 53 | { 54 | //The shortest sequence will never be longer than value below 55 | int maxSequenceLength = 2 * (int)Math.Floor(Math.Log(A, 2)); 56 | 57 | for (int i = 1; i <= maxSequenceLength; i++) 58 | { 59 | shortestSequence = new int[i]; 60 | shortestSequence[0] = 1; 61 | 62 | if (SearchDivision(shortestSequence, 0, A)) 63 | break; 64 | } 65 | } 66 | else 67 | throw new ArgumentOutOfRangeException("A", "The parameter must be an integer within the range [1..600]."); 68 | 69 | //Return the result 70 | return shortestSequence; 71 | } 72 | } -------------------------------------------------------------------------------- /Mu 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Mu 2011 5 | class Solution 6 | { 7 | #region Fields 8 | private const int _zeroCharacter = (int)'0'; 9 | private const int _nineCharacter = (int)'9'; 10 | private Dictionary _digits = new Dictionary() { { '0', 0 }, { '1', 1 }, { '2', 2 }, { '3', 3 }, { '4', 4 }, { '5', 5 }, { '6', 6 }, { '7', 7 }, { '8', 8 }, { '9', 9 } }; 11 | private ModularArithmetic _modularArithmetic = null; 12 | private const long _modulus = 1410000017; 13 | #endregion 14 | 15 | #region Constructor 16 | public Solution() 17 | { 18 | //Because potential numbers might be very big we will use modular arithmetic 19 | _modularArithmetic = new ModularArithmetic(_modulus); 20 | } 21 | #endregion 22 | 23 | 24 | public int solution(string S) 25 | { 26 | //This is where result will be stored 27 | long numberOfZeros = 0; 28 | 29 | //Sanity check 30 | if (!String.IsNullOrEmpty(S)) 31 | { 32 | int L = S.Length; 33 | 34 | if (L > 1 && S.StartsWith("0")) 35 | throw new ArgumentException("Parameter can't contain leading zeros", "S"); 36 | 37 | //We will store the value of the N in the modular arithmetic form 38 | long modularArithmeticNumber = 0; 39 | //We will also store the number of zeros in a decimal representation of N --> Z 40 | int numberOfZerosInNumber = 0; 41 | 42 | //We will go most to least significant. 43 | for (int i = 0; i < L; i++) 44 | { 45 | if (_zeroCharacter > S[i] || S[i] > _nineCharacter) 46 | throw new ArgumentException("Parameter can contain only digits (0-9)", "S"); 47 | 48 | //The current digit --> D 49 | int digit = _digits[S[i]]; 50 | 51 | //numberOfZeros(10 * N + D) = 10 * (numberOfZeros(N) - 1) + N - (9 - D)*Z + 1 52 | numberOfZeros = _modularArithmetic.Subtract(_modularArithmetic.Add(_modularArithmetic.Multiply(10, numberOfZeros), modularArithmeticNumber), _modularArithmetic.Multiply(numberOfZerosInNumber, 9 - digit)); 53 | 54 | if (digit == 0) 55 | numberOfZerosInNumber += 1; 56 | 57 | //The value of N at the end of the loop is previous value of N multiplied by 10 and increased by current digit 58 | modularArithmeticNumber = _modularArithmetic.Add(_modularArithmetic.Multiply(10, modularArithmeticNumber), digit); 59 | } 60 | } 61 | 62 | //Return the result 63 | return (int)_modularArithmetic.Add(numberOfZeros, 1); 64 | } 65 | } -------------------------------------------------------------------------------- /Upsilon 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Upsilon 2012 (based on Cartesian tree --> http://en.wikipedia.org/wiki/Cartesian_tree) 4 | class Solution 5 | { 6 | public int solution(int[] A) 7 | { 8 | //This is where result will be stored 9 | int maximumSequenceLength = 0; 10 | 11 | //Sanity check 12 | if (A != null && A.Length > 0 && A.Length <= 100000) 13 | { 14 | int N = A.Length; 15 | 16 | int[] guardedInput = new int[N + 1]; 17 | int[] rootsStack = new int[N + 2]; 18 | int[] heightsStack = new int[N + 2]; 19 | int stackPointer = 1; 20 | 21 | //We need to add a guard at the end of input table and initialize the stacks 22 | for (int i = 0; i < N; i++) 23 | { 24 | guardedInput[i] = A[i]; 25 | rootsStack[i] = Int32.MaxValue; 26 | heightsStack[i] = 0; 27 | } 28 | 29 | guardedInput[N] = Int32.MaxValue - 1; 30 | rootsStack[N] = Int32.MaxValue; 31 | heightsStack[N] = 0; 32 | rootsStack[N + 1] = Int32.MaxValue; 33 | heightsStack[N + 1] = 0; 34 | 35 | //We iterate through the guarded input table 36 | for (int i = 0; i <= N; i++) 37 | { 38 | //If current element is greater then the one at the top of the stack 39 | if (guardedInput[i] > rootsStack[stackPointer]) 40 | { 41 | //We have new root and all elements smaller than this will form its left sub-tree 42 | while (guardedInput[i] > rootsStack[stackPointer - 1]) 43 | { 44 | //We need to merge the two top items on the stack (by computing new height) 45 | heightsStack[stackPointer - 1] = Math.Max(heightsStack[stackPointer - 1], heightsStack[stackPointer] + 1); 46 | //End we need to pop one from the stuck 47 | stackPointer--; 48 | } 49 | 50 | //We put the new root to the stack 51 | rootsStack[stackPointer] = guardedInput[i]; 52 | heightsStack[stackPointer]++; 53 | } 54 | //Otherwise current element is a leaf 55 | else 56 | { 57 | stackPointer++; 58 | rootsStack[stackPointer] = guardedInput[i]; 59 | heightsStack[stackPointer] = 0; 60 | } 61 | } 62 | 63 | maximumSequenceLength = heightsStack[stackPointer]; 64 | } 65 | 66 | //Return the result 67 | return maximumSequenceLength; 68 | } 69 | } -------------------------------------------------------------------------------- /Beta 2010.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Beta 2010 4 | class Solution { 5 | public int solution(int[] A) { 6 | //This is where result will be stored 7 | int intersectingDiscs = 0; 8 | 9 | //Sanity check 10 | if (A != null && A.Length > 0) 11 | { 12 | int N = A.Length; 13 | 14 | //We will store information on how many discs starts and ends in every point 15 | int[] discsStarts = new int[N]; 16 | int[] discsEnds = new int[N]; 17 | 18 | //Calculating how many discs starts and ends in every point 19 | int farthestPossibleDiscsEnd = N - 1; 20 | for (int i = 0; i < A.Length; i++) 21 | { 22 | //If the disc is starting before the 0 point we will treat it as starting in 0 point 23 | discsStarts[i >= A[i] ? i - A[i] : 0]++; 24 | 25 | //If the disc is ending after the N - 1 point we will treat it as ending in N - 1 point. 26 | int discEnd = i + A[i]; 27 | //The discEnd < 0 check is for arithmetic overflow prevention 28 | discsEnds[(discEnd < 0 || discEnd >= N) ? farthestPossibleDiscsEnd : discEnd]++; 29 | } 30 | 31 | //This will keep track of discs that have started before the current point and will end in this point or after it 32 | int containingDiscs = 0; 33 | for (int i = 0; i < N; i++) 34 | { 35 | //We increase the number of intersecting discs by: 36 | //- the number of discs that have been started but not ended before current point multiplied by the number of discs starting at current point (all of them intersect with each other) 37 | intersectingDiscs += containingDiscs * discsStarts[i]; 38 | //- the number of discs starting at current point multiplied by number of discs starting at current point minus one and this divided by 2 (every disc starting at current point is intersecting with all the others discs starting at current point and we need to avoid counting double intersections) 39 | intersectingDiscs += (discsStarts[i] * (discsStarts[i] - 1))/2; 40 | 41 | //If the number of intersecting discs is above 10,000,000 we should return -1 42 | if (intersectingDiscs > 10000000) 43 | return -1; 44 | 45 | //We adjust the number of discs started before the current by adding the number of discs starting in current point and substracting the number of discs ending in current point 46 | //This way we keep the number of discs starting before or in current point which haven't end yet 47 | containingDiscs += discsStarts[i] - discsEnds[i]; 48 | } 49 | } 50 | 51 | //Return the result 52 | return intersectingDiscs; 53 | } 54 | } -------------------------------------------------------------------------------- /ModularArithmetic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | // Implementation of basic modular arithmetic (http://en.wikipedia.org/wiki/Modular_arithmetic) operations 4 | public class ModularArithmetic 5 | { 6 | #region Modulus 7 | private long _modulus; 8 | 9 | //The modular arithmetic requires different modulus operation than % operator provides 10 | private long CongruentModulo(long dividend) 11 | { 12 | return (dividend % _modulus + _modulus) % _modulus; 13 | } 14 | #endregion 15 | 16 | #region Extended Euclidean Algorithm 17 | private struct ExtendedEuclideanAlgorithmResult 18 | { 19 | 20 | public long GreatestCommonDivisor { get; private set; } 21 | 22 | public long X { get; private set; } 23 | 24 | public long Y { get; private set; } 25 | 26 | public ExtendedEuclideanAlgorithmResult(long greatestCommonDivisor, long x, long y) 27 | : this() 28 | { 29 | GreatestCommonDivisor = greatestCommonDivisor; 30 | X = x; 31 | Y = y; 32 | } 33 | } 34 | 35 | //Recursive Extended Euclidean algorithm (http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm) implementation 36 | private ExtendedEuclideanAlgorithmResult RecursiveExtendedEuclideanAlgorithm(long dividend, long divisor) 37 | { 38 | if (divisor == 0) 39 | return new ExtendedEuclideanAlgorithmResult(dividend, 1, 0); 40 | 41 | long quotient = dividend / divisor; 42 | long remainder = (dividend % divisor + divisor) % divisor; 43 | 44 | ExtendedEuclideanAlgorithmResult result = RecursiveExtendedEuclideanAlgorithm(divisor, remainder); 45 | 46 | return new ExtendedEuclideanAlgorithmResult(result.GreatestCommonDivisor, result.Y, result.X - (quotient * result.Y)); 47 | } 48 | #endregion 49 | 50 | #region Multiplicative Inverse 51 | //The modular multiplication inverse (http://en.wikipedia.org/wiki/Modular_multiplicative_inverse) is required for dividing in modular arithmetic 52 | private long ModularMultiplicativeInverse(long a) 53 | { 54 | //Find modular multiplication inverse with Extended Euclidean algorithm 55 | return RecursiveExtendedEuclideanAlgorithm(a, _modulus).X; 56 | } 57 | #endregion 58 | 59 | #region Constructor 60 | public ModularArithmetic(long modulus) 61 | { 62 | _modulus = modulus; 63 | } 64 | #endregion 65 | 66 | #region Operations 67 | public long Add(long firstAddend, long secondAddend) 68 | { 69 | return CongruentModulo(firstAddend + secondAddend); 70 | } 71 | 72 | public long Divide(long dividend, long divisor) 73 | { 74 | //We can't divide in modular arithmetic, we need to multiply by modular multiplication inverse 75 | return CongruentModulo(dividend * ModularMultiplicativeInverse(divisor)); 76 | } 77 | 78 | public long Multiply(long multiplicand, long multiplier) 79 | { 80 | return CongruentModulo(multiplicand * multiplier); 81 | } 82 | 83 | public long Subtract(long minuend, long subtrahend) 84 | { 85 | return CongruentModulo(minuend - subtrahend + _modulus); 86 | } 87 | #endregion 88 | } -------------------------------------------------------------------------------- /Omicron 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Omicron 2012 (based on Codility Blog --> http://blog.codility.com/2012/03/omicron-2012-codility-programming.html) 4 | class Solution 5 | { 6 | #region Fields 7 | private long[,] _fibonacciMatrix = new long[,] { { 1, 1 }, { 1, 0 } }; 8 | private long[,] _powMatrix = new long[,] { { 1, 0 }, { 0, 1 } }; 9 | 10 | private const long _modulus = 10000103; 11 | private const long _modulusPeriod = 20000208; 12 | private ModularArithmetic _modularArithmetic = null; 13 | #endregion 14 | 15 | #region Constructor 16 | public Solution() 17 | { 18 | //Because potential numbers might be very big we will use modular arithemtic 19 | _modularArithmetic = new ModularArithmetic(_modulus); 20 | } 21 | #endregion 22 | 23 | #region Methods 24 | private long[,] MultiplyMatrices(long[,] multiplicand, long[,] multiplier) 25 | { 26 | long[,] product = new long[2, 2]; 27 | 28 | product[0,0] = _modularArithmetic.Add(_modularArithmetic.Multiply(multiplicand[0,0], multiplier[0,0]), _modularArithmetic.Multiply(multiplicand[0,1], multiplier[1,0])); 29 | product[0,1] = _modularArithmetic.Add(_modularArithmetic.Multiply(multiplicand[0,0], multiplier[0,1]), _modularArithmetic.Multiply(multiplicand[0,1], multiplier[1,1])); 30 | product[1,0] = _modularArithmetic.Add(_modularArithmetic.Multiply(multiplicand[1,0], multiplier[0,0]), _modularArithmetic.Multiply(multiplicand[1,1], multiplier[1,0])); 31 | product[1,1] = _modularArithmetic.Add(_modularArithmetic.Multiply(multiplicand[1,0], multiplier[0,1]), _modularArithmetic.Multiply(multiplicand[1,1], multiplier[1,1])); 32 | 33 | return product; 34 | } 35 | 36 | private long[,] PowerMatrix(long[,] @base, long power) 37 | { 38 | long[,] product; 39 | 40 | if (power == 0) 41 | product = _powMatrix; 42 | else if (power % 2 == 0) 43 | product = PowerMatrix(MultiplyMatrices(@base, @base), power / 2); 44 | else 45 | product = MultiplyMatrices(PowerMatrix(@base, power - 1), @base); 46 | 47 | return product; 48 | } 49 | 50 | private long Fibonacci(long n) 51 | { 52 | return PowerMatrix(_fibonacciMatrix, n)[0, 1]; 53 | } 54 | 55 | private long PowerModModulusPeriod(long @base, long power, long modulusPeriod) 56 | { 57 | long product; 58 | 59 | if (power == 0) 60 | product = 1; 61 | else if (power % 2 == 0) 62 | product = PowerModModulusPeriod((@base * @base) % modulusPeriod, power / 2, modulusPeriod); 63 | else 64 | product = (@base * PowerModModulusPeriod(@base, power - 1, modulusPeriod)) % modulusPeriod; 65 | 66 | return product; 67 | } 68 | #endregion 69 | 70 | public int solution(int N, int M) 71 | { 72 | long powerFibonacci = 0; 73 | 74 | if (N < 0) 75 | throw new ArgumentOutOfRangeException("N", "N must be a non-negative integer."); 76 | 77 | if (M < 0) 78 | throw new ArgumentOutOfRangeException("M", "M must be a non-negative integer."); 79 | 80 | if (N == 0 && M != 0) 81 | powerFibonacci = 0; 82 | else if (N == 1 || M == 0) 83 | powerFibonacci = 1; 84 | else 85 | powerFibonacci = Fibonacci(PowerModModulusPeriod(N, M, _modulusPeriod)); 86 | 87 | return (int)powerFibonacci; 88 | } 89 | } -------------------------------------------------------------------------------- /Lambda 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Lambda 2011 (based on Codility solution --> http://blog.codility.com/2011/11/lambda-2011-certificate-solution.html) 4 | class Solution 5 | { 6 | public int solution(int[] T) 7 | { 8 | //This is where result will be stored 9 | int lowestPeripheralityRouter = -1; 10 | 11 | //Sanity check 12 | if (T != null && T.Length > 0) 13 | { 14 | int N = T.Length; 15 | 16 | //We will treat the routers network as a tree, first we calculate a degree of every node 17 | int[] routersDegrees = new int[N]; 18 | for (int i = 0; i < N; i++) 19 | { 20 | if (T[i] != i) 21 | routersDegrees[T[i]]++; 22 | } 23 | 24 | //Now we will order the nodes from leaves to root 25 | int routersOrderingIndex = 0; 26 | int[] routersOrdering = new int[N]; 27 | 28 | //Nodes with degree 0 are leaves 29 | for (int i = 0; i < N; i++) 30 | { 31 | if (routersDegrees[i] == 0) 32 | { 33 | routersOrdering[routersOrderingIndex++] = i; 34 | } 35 | } 36 | 37 | //When all descends of node has been order the node itself can be ordered 38 | for (int i = 0; i < N; i++) 39 | { 40 | int parentRouter = T[routersOrdering[i]]; 41 | routersDegrees[parentRouter]--; 42 | if (routersDegrees[parentRouter] == 0) 43 | { 44 | routersOrdering[routersOrderingIndex++] = parentRouter; 45 | } 46 | } 47 | 48 | int[] routersSubtreesLengths = new int[N]; 49 | for (int i = 0; i < N; i++) 50 | routersSubtreesLengths[i] = 1; 51 | 52 | int[] routersTotalPathsLengths = new int[N]; 53 | 54 | //First we treat every node as root and caculate the size of the subtree with this route and the total length of paths going down from this route 55 | for (int i = 0; i < N; i++) 56 | { 57 | int rootRouterDescendant = routersOrdering[i]; 58 | int rootRouter = T[rootRouterDescendant]; 59 | routersSubtreesLengths[rootRouter] = routersSubtreesLengths[rootRouter] + routersSubtreesLengths[rootRouterDescendant]; 60 | routersTotalPathsLengths[rootRouter] = routersTotalPathsLengths[rootRouter] + routersTotalPathsLengths[rootRouterDescendant] + routersSubtreesLengths[rootRouterDescendant]; 61 | } 62 | 63 | //Now we need to increase the total path lengths with paths going up from this route 64 | for (int i = N - 2; i >= 0; i--) 65 | { 66 | int rootRouter = routersOrdering[i]; 67 | int rootRouterParent = T[rootRouter]; 68 | routersTotalPathsLengths[rootRouter] = routersTotalPathsLengths[rootRouterParent] - routersSubtreesLengths[rootRouter] + N - routersSubtreesLengths[rootRouter]; 69 | } 70 | 71 | //Now we just need to find the shortest path 72 | lowestPeripheralityRouter = 0; 73 | for (int i = 0; i < N; i++) 74 | { 75 | if (routersTotalPathsLengths[i] < routersTotalPathsLengths[lowestPeripheralityRouter]) 76 | lowestPeripheralityRouter = i; 77 | } 78 | } 79 | 80 | //Return the result 81 | return lowestPeripheralityRouter; 82 | } 83 | } -------------------------------------------------------------------------------- /Zeta 2011 O(NM).cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Zeta 2011 (Dynamic Programming) 4 | class Solution 5 | { 6 | //Directions definitions 7 | private const int _bottomDirection = -1; 8 | private const int _rightDirection = 1; 9 | private const int _neutralDirection = 0; 10 | 11 | public int solution(int[][] A, int K) 12 | { 13 | //This is where result will be stored 14 | int result = 0; 15 | 16 | //Sanity check 17 | if (A != null && A.Length > 0 && K > 0) 18 | { 19 | int N = A.Length; 20 | int M = A[0].Length; 21 | 22 | //We will keep the number of ball exiting the board through bottom edge in every column 23 | int[] numberOfBallsExitingSwitchBottom = new int[M]; 24 | 25 | //First we assume that all the balls exits through first column (as they enter through it) 26 | numberOfBallsExitingSwitchBottom[0] = K; 27 | 28 | //We will move first through rows than through columns (thanks to that in the end the numberOfBallsExitingBottom will contain numbers for last row) 29 | for (int switchRowIndex = 0; switchRowIndex < N; switchRowIndex++) 30 | { 31 | //We need to keep the number of balls exiting the switch through the right edge 32 | int numberOfBallsExitingSwitchRight = 0; 33 | 34 | for (int switchColumnIndex = 0; switchColumnIndex < M; switchColumnIndex++) 35 | { 36 | //The number of balls entering current switch is equal to the sum of balls exiting switch through bottom edge in the same column from previous row and the balls exiting switch through right edge in previous column of the same row 37 | int numberOfBallsEnteringSwitch = numberOfBallsExitingSwitchBottom[switchColumnIndex] + numberOfBallsExitingSwitchRight; 38 | 39 | int switchDirection = A[switchRowIndex][switchColumnIndex]; 40 | //If this switch doesn't change direction of the ball then number of balls exiting through bottom and right edge is already correct (the same as previous switches) 41 | if (switchDirection != _neutralDirection) 42 | { 43 | //If initial direction of this ball switch is "to the right" 44 | if (switchDirection == _rightDirection) 45 | //Then every even ball entering it will exit through the bottom edge 46 | numberOfBallsExitingSwitchBottom[switchColumnIndex] = numberOfBallsEnteringSwitch / 2; 47 | //If initial direction of this ball switch is "to the bottom" 48 | else 49 | //Then every odd ball entering it will exit through the bottom edge 50 | numberOfBallsExitingSwitchBottom[switchColumnIndex] = numberOfBallsEnteringSwitch / 2 + numberOfBallsEnteringSwitch % 2; 51 | 52 | //The number of balls exiting switch through the right edge is equal to number of balls entering minus the balls which have exited through bottom edge 53 | numberOfBallsExitingSwitchRight = numberOfBallsEnteringSwitch - numberOfBallsExitingSwitchBottom[switchColumnIndex]; 54 | } 55 | } 56 | } 57 | 58 | //The final result is the number of balls exiting the board trough the bottom edge of the bottom-right switch 59 | result = numberOfBallsExitingSwitchBottom[M - 1]; 60 | } 61 | 62 | return result; 63 | } 64 | } -------------------------------------------------------------------------------- /Phi 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Phi 2012 (based on Codility Blog --> http://blog.codility.com/2012/10/phi-2012-codility-programming.html) 4 | class Solution 5 | { 6 | #region Fields 7 | private const long _modulus = 10000007; 8 | private ModularArithmetic _modularArithmetic = null; 9 | #endregion 10 | 11 | #region Constructor 12 | public Solution() 13 | { 14 | //Because potential numbers might be very big we will use modular arithmetic 15 | _modularArithmetic = new ModularArithmetic(_modulus); 16 | } 17 | #endregion 18 | 19 | #region Methods 20 | private bool IsFeasibleBitVector(int x) 21 | { 22 | bool isFeasibleBitVector = true; 23 | 24 | while(x > 0) 25 | { 26 | if (x % 2 == 1) 27 | isFeasibleBitVector = !isFeasibleBitVector; 28 | else if (!isFeasibleBitVector) 29 | break; 30 | 31 | x = x / 2; 32 | } 33 | 34 | return isFeasibleBitVector; 35 | } 36 | 37 | private long[,] GetPossibleCombinationsMatrix(int M) 38 | { 39 | int matrixDimension = (int)Math.Pow(2, M); 40 | long[,] possibleCombinationsMatrix = new long[matrixDimension, matrixDimension]; 41 | 42 | for (int i = 0; i < matrixDimension; i++) 43 | { 44 | if (IsFeasibleBitVector(i)) 45 | { 46 | for (int j = 0; j < matrixDimension; j++) 47 | { 48 | if (IsFeasibleBitVector(j) && (i & j) == 0) 49 | possibleCombinationsMatrix[i, j] = 1; 50 | } 51 | } 52 | } 53 | 54 | return possibleCombinationsMatrix; 55 | } 56 | 57 | private long[,] GetIdentityMatrix(int size) 58 | { 59 | long[,] identityMatrix = new long[size, size]; 60 | 61 | for (int i = 0; i < size; i++) 62 | identityMatrix[i, i] = 1; 63 | 64 | return identityMatrix; 65 | } 66 | 67 | private long[,] MultiplyMatrices(long[,] multiplicand, long[,] multiplier) 68 | { 69 | int length = multiplicand.GetLength(0); 70 | long[,] product = new long[length, length]; 71 | 72 | for (int i = 0; i < length; i++) 73 | { 74 | for (int j = 0; j < length; j++) 75 | { 76 | long currentProduct = 0; 77 | 78 | for (int k = 0; k < length; k++) 79 | currentProduct = _modularArithmetic.Add(currentProduct, _modularArithmetic.Multiply(multiplicand[i, k], multiplier[k, j])); 80 | 81 | product[i, j] = currentProduct; 82 | } 83 | } 84 | 85 | return product; 86 | } 87 | 88 | private long[,] PowerMatrix(long[,] @base, long power) 89 | { 90 | long[,] product = GetIdentityMatrix(@base.GetLength(0)); 91 | 92 | if (power == 0) 93 | product = GetIdentityMatrix(@base.GetLength(0)); 94 | else if (power % 2 == 0) 95 | product = PowerMatrix(MultiplyMatrices(@base, @base), power / 2); 96 | else 97 | product = MultiplyMatrices(PowerMatrix(@base, power - 1), @base); 98 | 99 | return product; 100 | } 101 | #endregion 102 | 103 | public int solution(int N, int M) 104 | { 105 | if (N < 1 || N > 1000000) 106 | throw new ArgumentOutOfRangeException("N", "N must be an integer within the range [1..1,000,000]."); 107 | 108 | if (M < 1 || M > 7) 109 | throw new ArgumentOutOfRangeException("M", "M must be an integer within the range [1..7]."); 110 | 111 | return (int)PowerMatrix(GetPossibleCombinationsMatrix(M), N)[0, 0]; 112 | } 113 | } -------------------------------------------------------------------------------- /Eta 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Eta 2011 4 | class Solution 5 | { 6 | public int solution(int[] A) 7 | { 8 | //With the incremental construction or some other methods you can prove that graphs build according to the rules in task can have only three Hamiltonian paths 9 | int hamiltonianRoutesCount = 3; 10 | 11 | int N = A.Length; 12 | int M = (N / 2) + 1; 13 | 14 | //We will store the number of town occurences in the entire route 15 | int[] townOccurancesInRoute = new int[M]; 16 | //We will store the number of how many times the road has been taken (we will look at roads from "left to right") 17 | int[,] townRoadsTaken = new int[M, 3]; 18 | 19 | //We need to check if any of the rules has been violeted 20 | for (int i = 0; i < N && hamiltonianRoutesCount == 3; i++) 21 | { 22 | townOccurancesInRoute[A[i]]++; 23 | int roadBeginningTown = A[i]; 24 | int roadDestinationTown = A[(i + 1) % N]; 25 | 26 | //Each road must connect distinct towns 27 | if (roadBeginningTown == roadDestinationTown) 28 | hamiltonianRoutesCount = -2; 29 | //We always want to look at road from "letf to right" 30 | else 31 | { 32 | if (roadBeginningTown > roadDestinationTown) 33 | { 34 | roadBeginningTown = roadDestinationTown; 35 | roadDestinationTown = A[i]; 36 | } 37 | 38 | for (int j = 0; j < 3; j++) 39 | { 40 | //If the town doesn't have the road defined at its j exit 41 | if (townRoadsTaken[roadBeginningTown, j] == 0) 42 | //We mark a new road from this town to destination town as visited once 43 | townRoadsTaken[roadBeginningTown, j] = roadDestinationTown; 44 | //If the road defined at towns j exit is the road to the destination town and it has been visited once 45 | else if (townRoadsTaken[roadBeginningTown, j] == roadDestinationTown) 46 | //We mark a road from this town to destination town as visited twice (by negating the destination town id) 47 | townRoadsTaken[roadBeginningTown, j] = -roadDestinationTown; 48 | //If the road defined at towns j exit is the road to the destination town and it has been visited twice 49 | else if (townRoadsTaken[roadBeginningTown, j] == -roadDestinationTown) 50 | { 51 | //We have an error, each road must be taken exactly twice 52 | hamiltonianRoutesCount = -2; 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | 59 | //If everything has been good so far we need to do some more checking 60 | if (hamiltonianRoutesCount == 3) 61 | { 62 | for (int i = 0; i < M; i++) 63 | { 64 | //Each town must be visited either exactly once or exactly thrice 65 | if (townOccurancesInRoute[i] != 1 && townOccurancesInRoute[i] != 3) 66 | { 67 | hamiltonianRoutesCount = -2; 68 | break; 69 | } 70 | 71 | //Each road must be taken exactly twice 72 | for (int j = 0; j < 3; j++) 73 | { 74 | if (townRoadsTaken[i, j] > 0) 75 | { 76 | hamiltonianRoutesCount = -2; 77 | break; 78 | } 79 | } 80 | } 81 | } 82 | 83 | //Return the result 84 | return hamiltonianRoutesCount; 85 | } 86 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Codility 2 | ======== 3 | 4 | Solutions to [Codility](http://codility.com/) programming [challenges](https://codility.com/programmers/challenges/): 5 | 6 | - [x] [Alpha 2010](https://codility.com/programmers/challenges/alpha2010) 7 | - [x] [Beta 2010](https://codility.com/programmers/challenges/beta2010) 8 | - [x] [Gamma 2011](https://codility.com/programmers/challenges/gamma2011) 9 | - [x] [Delta 2011](https://codility.com/programmers/challenges/delta2011) 10 | - [x] [Epsilon 2011](https://codility.com/programmers/challenges/epsilon2011) 11 | - [x] [Zeta 2011](https://codility.com/programmers/challenges/zeta2011) 12 | - [x] [Eta 2011](https://codility.com/programmers/challenges/eta2011) 13 | - [x] [Theta 2011](https://codility.com/programmers/challenges/theta2011) 14 | - [x] [Iota 2011](https://codility.com/programmers/challenges/iota2011) 15 | - [x] [Kappa 2011](https://codility.com/programmers/challenges/kappa2011) 16 | - [x] [Lambda 2011](https://codility.com/programmers/challenges/lambda2011) 17 | - [x] [Mu 2011](https://codility.com/programmers/challenges/mu2011) 18 | - [x] [Nu 2011](https://codility.com/programmers/challenges/nu2011) 19 | - [x] [Xi 2012](https://codility.com/programmers/challenges/xi2012) 20 | - [x] [Omicron 2012](https://codility.com/programmers/challenges/omicron2012) 21 | - [x] [Pi 2012](https://codility.com/programmers/challenges/pi2012) 22 | - [x] [Rho 2012](https://codility.com/programmers/challenges/rho2012) 23 | - [x] [Sigma 2012](https://codility.com/programmers/challenges/sigma2012) 24 | - [x] [Tau 2012](https://codility.com/programmers/challenges/tau2012) 25 | - [x] [Upsilon 2012](https://codility.com/programmers/challenges/upsilon2012) 26 | - [x] [Phi 2012](https://codility.com/programmers/challenges/phi2012) 27 | - [x] [Chi 2012](https://codility.com/programmers/challenges/chi2012) 28 | - [ ] [Psi 2012](https://codility.com/programmers/challenges/psi2012) 29 | - [ ] [Omega 2013](https://codility.com/programmers/challenges/omega2013) 30 | - [ ] [Hydrogenium 2013](https://codility.com/programmers/challenges/hydrogenium2013) 31 | - [ ] [Helium 2013](https://codility.com/programmers/challenges/helium2013) 32 | - [ ] [Lithium 2013](https://codility.com/programmers/challenges/lithium2013) 33 | - [ ] [Beryllium 2013](https://codility.com/programmers/challenges/beryllium2013) 34 | - [ ] [Boron 2013](https://codility.com/programmers/challenges/boron2013) 35 | - [ ] [Carbo 2013](https://codility.com/programmers/challenges/carbo2013) 36 | - [ ] [Nitrogenium 2013](https://codility.com/programmers/challenges/nitrogenium2013) 37 | - [ ] [Oxygenium 2014](https://codility.com/programmers/challenges/oxygenium2014) 38 | - [ ] [Fluorum 2014](https://codility.com/programmers/challenges/fluorum2014) 39 | - [ ] [Neon 2014](https://codility.com/programmers/challenges/neon2014) 40 | - [ ] [Natrium 2014](https://codility.com/programmers/challenges/natrium2014) 41 | - [ ] [Magnesium 2014](https://codility.com/programmers/challenges/magnesium2014) 42 | - [ ] [Aluminium 2014](https://codility.com/programmers/challenges/aluminium2014) 43 | - [ ] [Silicium 2014](https://codility.com/programmers/challenges/silicium2014) 44 | - [ ] [Phosphorus 2014](https://codility.com/programmers/challenges/phosphorus2014) 45 | 46 | ## Donating 47 | 48 | My blog and open source projects are result of my passion for software development, but they require a fair amount of my personal time. If you got value from any of the content I create, then I would appreciate your support by [buying me a coffee](https://www.buymeacoffee.com/tpeczek). 49 | 50 | 51 | 52 | ## Copyright and License 53 | 54 | Copyright © 2013 - 2020 Tomasz Pęczek 55 | 56 | Licensed under the [MIT License](https://github.com/tpeczek/Codility/blob/master/LICENSE.md) 57 | -------------------------------------------------------------------------------- /Iota 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Iota 2011 5 | class Solution 6 | { 7 | public int solution(int[] A) 8 | { 9 | //This is where result will be stored 10 | int shortestAdjacentSequenceLength = 0; 11 | 12 | //Sanity check 13 | if (A != null && A.Length > 0) 14 | { 15 | int N = A.Length; 16 | int firstElement = A[0]; 17 | int lastElement = A[N - 1]; 18 | 19 | //First possible edge case - first element is the same as last (also a single element situation) 20 | if (firstElement == lastElement) 21 | shortestAdjacentSequenceLength = 1; 22 | else 23 | { 24 | //Second possible edge case - array containing two different elements (could go through the main algorithm but why waste time) 25 | shortestAdjacentSequenceLength = 2; 26 | if (N > 2) 27 | { 28 | //All adjacent elements of given element 29 | Dictionary> adjacentElements = new Dictionary> { { firstElement, new HashSet { A[1] } }, { lastElement, new HashSet { A[N - 2] } } }; 30 | //Has the given element been already added to any sequence 31 | Dictionary elementConsideredForSequence = new Dictionary { { firstElement, true }, { lastElement, false } }; 32 | for (int i = 1; i < N - 1; i++) 33 | { 34 | if (!adjacentElements.ContainsKey(A[i])) 35 | { 36 | adjacentElements.Add(A[i], new HashSet()); 37 | elementConsideredForSequence.Add(A[i], false); 38 | } 39 | adjacentElements[A[i]].Add(A[i - 1]); 40 | adjacentElements[A[i]].Add(A[i + 1]); 41 | } 42 | 43 | //We will keep candidates for our sequence separated between current and next ones 44 | List currentAdjacentSequencesCandidates = new List { firstElement }; 45 | List nextAdjacentSequencesCandidates = new List(); 46 | 47 | //We will go until we reach the last element 48 | while (!elementConsideredForSequence[lastElement]) 49 | { 50 | //If we have run out of the candidates in "current" collection we need to move to the "next" element and examine candidates on that position 51 | if (currentAdjacentSequencesCandidates.Count == 0) 52 | { 53 | shortestAdjacentSequenceLength++; 54 | List tempDdjacentSequencesCandidates = currentAdjacentSequencesCandidates; 55 | currentAdjacentSequencesCandidates = nextAdjacentSequencesCandidates; 56 | nextAdjacentSequencesCandidates = tempDdjacentSequencesCandidates; 57 | } 58 | 59 | //We take the candidate out ot the "current" collection 60 | int currentCandidate = currentAdjacentSequencesCandidates[0]; 61 | currentAdjacentSequencesCandidates.RemoveAt(0); 62 | 63 | //We go through all adjacent elements of current candidate 64 | foreach (int possibleCandidate in adjacentElements[currentCandidate]) 65 | { 66 | //If this element hasn't been considered for any sequence yet 67 | if (!elementConsideredForSequence[possibleCandidate]) 68 | { 69 | //We add it as one of next possible candidates 70 | elementConsideredForSequence[possibleCandidate] = true; 71 | nextAdjacentSequencesCandidates.Add(possibleCandidate); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | //Return the result 80 | return shortestAdjacentSequenceLength; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Nu 2011 O(K(log(K)+log(N)+log(M))).cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Nu 2011 (based on Codility Blog --> http://blog.codility.com/2012/01/nu-2011-certificate-solution.html) 4 | class Solution 5 | { 6 | //Algorithm of the five(s) (http://en.wikipedia.org/wiki/Median_of_medians) adjusted to special conditions of this task 7 | private int AlgorithmOfTheFive(int[] A, int[] B, int P, int Q, int R, int S) 8 | { 9 | int sequenceMedian = 0; 10 | 11 | //We pre calculate the length of the target sequence 12 | int sequenceLength = (Q - P + 1) + (S - R + 1); 13 | 14 | if (sequenceLength <= 5) 15 | { 16 | //We will be dealing only with sequences of odd length, so we know at which index the median will be 17 | int sequenceMedianIndex = sequenceLength / 2; 18 | 19 | //We go only through first half of the new sequence 20 | for (int sequenceIndex = 0; sequenceIndex <= sequenceMedianIndex; sequenceIndex++) 21 | { 22 | //We take element from either A or B depending on their values (this way we can keep the target sequence sorted) 23 | if (P > Q || (R <= S && B[R] < A[P])) 24 | { 25 | sequenceMedian = B[R]; 26 | R++; 27 | } 28 | else 29 | { 30 | sequenceMedian = A[P]; 31 | P++; 32 | } 33 | } 34 | } 35 | else 36 | { 37 | int numberOfElementsToDrop = sequenceLength / 4; 38 | if (A[P + numberOfElementsToDrop] < B [S - numberOfElementsToDrop]) 39 | sequenceMedian = AlgorithmOfTheFive(A, B, P + numberOfElementsToDrop, Q, R, S - numberOfElementsToDrop); 40 | else 41 | sequenceMedian = AlgorithmOfTheFive(A, B, P, Q - numberOfElementsToDrop, R + numberOfElementsToDrop, S); 42 | } 43 | 44 | return sequenceMedian; 45 | } 46 | 47 | public int solution(int[] A, int[] B, int[] P, int[] Q, int[] R, int[] S) 48 | { 49 | //This is where result will be stored 50 | int sequencesMediansMedian = 0; 51 | 52 | //Sanity check 53 | if (A != null && A.Length > 0 && B != null && B.Length > 0 && P != null && Q != null && R != null && S != null && P.Length == Q.Length && P.Length == R.Length && P.Length == S.Length && P.Length != 0) 54 | { 55 | int N = A.Length; 56 | int M = B.Length; 57 | int K = P.Length; 58 | 59 | int[] sequencesMedians = new int[K]; 60 | for (int I = 0; I < K; I++) 61 | { 62 | int leftSequenceLength = Q[I] - P[I] + 1; 63 | int rightSequenceLength = S[I] - R[I] + 1; 64 | //We want to eliminate leftSequenceLength - rightSequenceLength - 1 elements from the first sequence, or rightSequenceLength - leftSequenceLength - 1 from second sequence 65 | int numberOfElementsToTruncate = Math.Abs(leftSequenceLength - rightSequenceLength) / 2; 66 | 67 | if (leftSequenceLength > rightSequenceLength) 68 | { 69 | if (A[P[I] + numberOfElementsToTruncate] >= B[S[I]]) 70 | sequencesMedians[I] = A[P[I] + numberOfElementsToTruncate]; 71 | else if (A[Q[I] - numberOfElementsToTruncate] <= B[R[I]]) 72 | sequencesMedians[I] = A[Q[I] - numberOfElementsToTruncate]; 73 | else 74 | sequencesMedians[I] = AlgorithmOfTheFive(A, B, P[I] + numberOfElementsToTruncate, Q[I] - numberOfElementsToTruncate, R[I], S[I]); 75 | } 76 | else 77 | { 78 | if (B[R[I] + numberOfElementsToTruncate] >= A[Q[I]]) 79 | sequencesMedians[I] = B[R[I] + numberOfElementsToTruncate]; 80 | else if (B[S[I] - numberOfElementsToTruncate] <= A[P[I]]) 81 | sequencesMedians[I] = B[S[I] - numberOfElementsToTruncate]; 82 | else 83 | sequencesMedians[I] = AlgorithmOfTheFive(A, B, P[I], Q[I], R[I] + numberOfElementsToTruncate, S[I] - numberOfElementsToTruncate); 84 | } 85 | 86 | } 87 | Array.Sort(sequencesMedians); 88 | sequencesMediansMedian = sequencesMedians[K / 2]; 89 | } 90 | 91 | //Return the result 92 | return sequencesMediansMedian; 93 | } 94 | } -------------------------------------------------------------------------------- /Theta 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Theta 2011 4 | class Solution 5 | { 6 | public int solution(int[] D, int[] P, int T) 7 | { 8 | //This is where result will be stored 9 | int cheapestRefillStrategyCost = -1; 10 | 11 | //Sanity check 12 | if (D != null && D.Length > 0 && P != null && P.Length == D.Length && T > 0) 13 | { 14 | int N = D.Length; 15 | cheapestRefillStrategyCost = 0; 16 | 17 | //For every town we will find farthest reachable town (with full tank). 18 | int[] farthestReachableTowns = new int[N]; 19 | for (int gas = T, currentTown = 0, farthestReachableTown = 0; currentTown < N; currentTown++) 20 | { 21 | //We will go through towns till the gas runs out 22 | while (farthestReachableTown < N && gas >= D[farthestReachableTown]) 23 | gas -= D[farthestReachableTown++]; 24 | 25 | //If we couldn't reach any town, no valid refill strategy exists 26 | if (farthestReachableTown == currentTown) 27 | { 28 | cheapestRefillStrategyCost = -1; 29 | break; 30 | } 31 | //Otherwise we mark the farthest reachable town and add enough gas to reach next town, we will look if we can reach any further with this gas from there. 32 | else 33 | { 34 | farthestReachableTowns[currentTown] = farthestReachableTown; 35 | gas += D[currentTown]; 36 | } 37 | } 38 | 39 | //If valid refill strategy exists 40 | if (cheapestRefillStrategyCost == 0) 41 | { 42 | //For every town we will find next cheaper town 43 | int[] cheaperTowns = new int[N]; 44 | cheaperTowns[N - 1] = N; 45 | 46 | //We will go back from town one before last 47 | for (int currentTown = N - 2; currentTown >= 0; currentTown--) 48 | { 49 | //If gas price in next town is lower than the current town 50 | if (P[currentTown + 1] < P[currentTown]) 51 | //We mark the next town as next cheaper gas station 52 | cheaperTowns[currentTown] = currentTown + 1; 53 | //Otherwise 54 | else 55 | { 56 | //We look for cheaper gas station among the ones we have already found 57 | cheaperTowns[currentTown] = cheaperTowns[currentTown + 1]; 58 | while (cheaperTowns[currentTown] != N && P[cheaperTowns[currentTown]] >= P[currentTown]) 59 | cheaperTowns[currentTown] = cheaperTowns[cheaperTowns[currentTown]]; 60 | 61 | } 62 | } 63 | 64 | //Creating refill strategy 65 | for (int gas = 0, gasRefill = 0, currentTown = 0, nextTown = 0; currentTown < N && cheapestRefillStrategyCost != -2; currentTown = nextTown) 66 | { 67 | //If next cheaper town is not reachable from this town 68 | if (cheaperTowns[currentTown] > farthestReachableTowns[currentTown]) 69 | { 70 | //We fill the full tank 71 | gasRefill = T - gas; 72 | 73 | //And move to the next town 74 | nextTown = currentTown + 1; 75 | gas = T - D[currentTown]; 76 | } 77 | //If next cheaper town is reachable from this town 78 | else 79 | { 80 | //We calculate how much gas we need to reach it 81 | int gasNeeded = 0; 82 | for (int passedTown = currentTown; passedTown < cheaperTowns[currentTown]; passedTown++) 83 | gasNeeded += D[passedTown]; 84 | 85 | //Make the required refill 86 | gasRefill = gas < gasNeeded ? gasNeeded - gas : 0; 87 | 88 | //And move to that town 89 | nextTown = cheaperTowns[currentTown]; 90 | gas += gasRefill - gasNeeded; 91 | } 92 | 93 | if ((ulong)cheapestRefillStrategyCost + (ulong)P[currentTown] * (ulong)gasRefill > 1000000000) 94 | cheapestRefillStrategyCost = -2; 95 | else 96 | cheapestRefillStrategyCost += P[currentTown] * gasRefill; 97 | } 98 | } 99 | } 100 | 101 | return cheapestRefillStrategyCost; 102 | } 103 | } -------------------------------------------------------------------------------- /Tau 2012.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | //Tau 2012 (based on Codility Blog --> http://blog.codility.com/2012/07/tau-2012-codility-programming.html) 4 | class Solution 5 | { 6 | private int[][] Transpose(int[][] C, int M, int N) 7 | { 8 | int[][] transposedC = new int[N][]; 9 | 10 | for (int i = 0; i < N; i++) 11 | { 12 | transposedC[i] = new int[M]; 13 | for (int j = 0; j < M; j++) 14 | { 15 | transposedC[i][j] = C[j][i]; 16 | } 17 | } 18 | 19 | return transposedC; 20 | } 21 | 22 | public int solution(int[][] C) 23 | { 24 | //This is where result will be stored 25 | int maximumProfit = 0; 26 | 27 | if (C != null && C.Length > 0 && C.Length <= 100 && C[0] != null && C[0].Length > 0 && C[0].Length <= 100) 28 | { 29 | int M = C.Length; 30 | int N = C[0].Length; 31 | 32 | if (M > N) 33 | { 34 | C = Transpose(C, M, N); 35 | M = C.Length; 36 | N = C[0].Length; 37 | } 38 | 39 | //The value of precalcualtedProfits[i][j] will be profit of a rectangle [0..i-1]x[0..j-1] 40 | int[][] precalcualtedProfits = new int[M + 1][]; 41 | precalcualtedProfits[0] = new int[N + 1]; 42 | for (int i = 1; i < M + 1; i++) 43 | { 44 | precalcualtedProfits[i] = new int[N + 1]; 45 | for (int j = 1; j < N + 1; j++) 46 | precalcualtedProfits[i][j] = precalcualtedProfits[i - 1][j] + precalcualtedProfits[i][j - 1] - precalcualtedProfits[i - 1][j - 1] + C[i - 1][j - 1]; 47 | } 48 | 49 | for (int i = 0; i < M; i++) 50 | { 51 | for (int k = i + 1; k < M + 1; k++) 52 | { 53 | int minimumInnerRectangleProfit = 0; //Minimum profit of a rectangle [i..k-1]x[0..j-1] 54 | int minimumInnerRectangleRow = 0; //Maximum such j 55 | int maximumMinimumInnerRectangleRemainderProfit = 0; //Maximum profit of a rectangle [i..k-1]x[j'..j-1] 56 | 57 | int maximumInnerRectangleProfit = 0; //Maximum profit of a rectangle [i..k-1]x[0..j-1] 58 | int maximumInnerRectangleRow = 0; //Minimum such j 59 | int minimumMaximumInnerRectangleRemainderProfit = 0; //Minimum profit of a rectangle [i..k-1]x[j'..j-1] 60 | 61 | int minimumOuterRectangleProfit = 0; //Minimum profit of a rectangle [0..i-1,k..M-1]x[0..j-1] 62 | int minimumOuterRectangleRow = 0; //Maximum such j 63 | int maximumMinimumOuterRectangleReminderProfit = 0; //Maximum profit of a rectangle [0..i-1,k..M-1]x[j'..j-1] 64 | 65 | int maximumOuterRectangleProfit = 0; //Maximum profit of a rectangle [0..i-1,k..M-1]x[0..j-1] 66 | int maximumOuterRectangleRow = 0; //Minimum such j 67 | int minimumMaximumOuterRectangleReminderProfit = 0; //Minimum profit of a rectangle [0..i-1,k..M-1]x[j'..j-1] 68 | 69 | int innerRectangleProfit = 0, outerRectangleProfit = 0; 70 | for (int j = 1; j < N + 1; j++) 71 | { 72 | innerRectangleProfit = precalcualtedProfits[k][j] - precalcualtedProfits[i][j]; 73 | outerRectangleProfit = precalcualtedProfits[M][j] - innerRectangleProfit; 74 | 75 | if (innerRectangleProfit <= minimumInnerRectangleProfit) 76 | { 77 | minimumInnerRectangleProfit = innerRectangleProfit; 78 | minimumInnerRectangleRow = j; 79 | } 80 | 81 | if (innerRectangleProfit - minimumInnerRectangleProfit > maximumMinimumInnerRectangleRemainderProfit) 82 | maximumMinimumInnerRectangleRemainderProfit = innerRectangleProfit - minimumInnerRectangleProfit; 83 | 84 | if (innerRectangleProfit > maximumInnerRectangleProfit) 85 | { 86 | maximumInnerRectangleProfit = innerRectangleProfit; 87 | maximumInnerRectangleRow = j; 88 | } 89 | 90 | if (innerRectangleProfit - maximumInnerRectangleProfit < minimumMaximumInnerRectangleRemainderProfit) 91 | minimumMaximumInnerRectangleRemainderProfit = innerRectangleProfit - maximumInnerRectangleProfit; 92 | 93 | 94 | 95 | if (outerRectangleProfit <= minimumOuterRectangleProfit) 96 | { 97 | minimumOuterRectangleProfit = outerRectangleProfit; 98 | minimumOuterRectangleRow = j; 99 | } 100 | 101 | if (outerRectangleProfit - minimumOuterRectangleProfit > maximumMinimumOuterRectangleReminderProfit) 102 | maximumMinimumOuterRectangleReminderProfit = outerRectangleProfit - minimumOuterRectangleProfit; 103 | 104 | if (outerRectangleProfit > maximumOuterRectangleProfit) 105 | { 106 | maximumOuterRectangleProfit = outerRectangleProfit; 107 | maximumOuterRectangleRow = j; 108 | } 109 | 110 | if (outerRectangleProfit - maximumOuterRectangleProfit < minimumMaximumOuterRectangleReminderProfit) 111 | minimumMaximumOuterRectangleReminderProfit = outerRectangleProfit - maximumOuterRectangleProfit; 112 | } 113 | maximumProfit = Math.Max(maximumProfit, Math.Max(Math.Max(maximumMinimumInnerRectangleRemainderProfit, maximumMinimumOuterRectangleReminderProfit), Math.Max(innerRectangleProfit - minimumMaximumInnerRectangleRemainderProfit, outerRectangleProfit - minimumMaximumOuterRectangleReminderProfit))); 114 | } 115 | } 116 | } 117 | 118 | //Return the result 119 | return maximumProfit; 120 | } 121 | } -------------------------------------------------------------------------------- /Epsilon 2011.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | //Epsilon 2011 5 | class Solution 6 | { 7 | #region Private Structs & Classes 8 | //Represents cartesian coordinates in two dimensions 9 | private struct CartesianCoordinates 10 | { 11 | #region Properties 12 | public double X { get; private set; } 13 | 14 | public double Y { get; private set; } 15 | #endregion 16 | 17 | #region Constructor 18 | public CartesianCoordinates(double x, double y) 19 | : this() 20 | { 21 | X = x; 22 | Y = y; 23 | } 24 | #endregion 25 | } 26 | 27 | //Represents linear function 28 | private class LinearFunction : IComparable, IComparable 29 | { 30 | #region Properties 31 | public int Slope { get; private set; } 32 | 33 | public int Intercept { get; private set; } 34 | #endregion 35 | 36 | #region Constructor 37 | public LinearFunction(int slope, int intercept) 38 | { 39 | Slope = slope; 40 | Intercept = intercept; 41 | } 42 | #endregion 43 | 44 | #region Methods 45 | public double Evaluate(double x) 46 | { 47 | return Slope * x + Intercept; 48 | } 49 | 50 | public CartesianCoordinates? Intersect(LinearFunction other) 51 | { 52 | if (this.Slope == other.Slope) 53 | return null; 54 | 55 | double x = (double)(this.Intercept - other.Intercept) / (double)(other.Slope - this.Slope); 56 | return new CartesianCoordinates(x, this.Evaluate(x)); 57 | } 58 | #endregion 59 | 60 | #region IComparable Members 61 | public int CompareTo(LinearFunction other) 62 | { 63 | //If slope and intercept are equal we treat the functions as equal 64 | if (this.Slope == other.Slope && this.Intercept == other.Intercept) 65 | return 0; 66 | //Functions with smaller slope first, if slope is equal the larger intercept goes first 67 | else if (this.Slope < other.Slope || (this.Slope == other.Slope && this.Intercept > other.Intercept)) 68 | return -1; 69 | else 70 | return 1; 71 | } 72 | 73 | public int CompareTo(object obj) 74 | { 75 | if (obj == null) 76 | throw new ArgumentNullException("obj"); 77 | if (!(obj is LinearFunction)) 78 | throw new ArgumentException("Object must be of type LinearFunction", "obj"); 79 | 80 | return CompareTo((LinearFunction)obj); 81 | } 82 | #endregion 83 | } 84 | 85 | //Represents envelope for collection of linear functions (envelope consist of lines and intersection points at which those lines connect) 86 | private class Envelope 87 | { 88 | #region Properties 89 | public LinearFunction[] Lines { get; private set; } 90 | 91 | public CartesianCoordinates[] Intersections { get; private set; } 92 | #endregion 93 | 94 | #region Constructor 95 | public Envelope(LinearFunction[] lines, CartesianCoordinates[] intersections) 96 | { 97 | Lines = lines; 98 | Intersections = intersections; 99 | } 100 | #endregion 101 | } 102 | #endregion 103 | 104 | #region Private Enums 105 | private enum EnvelopeKind 106 | { 107 | Upper, 108 | Lower 109 | } 110 | #endregion 111 | 112 | #region Fields 113 | private int _leftBorder = -10000000; 114 | private int _rightBorder = 10000000; 115 | #endregion 116 | 117 | #region Public Methods 118 | public double solution(int[] A, int[] B) 119 | { 120 | //This is where result will be stored 121 | double result = Double.MaxValue; 122 | 123 | //Sanity check 124 | if (A != null && A.Length > 0 && B != null && B.Length > 0) 125 | { 126 | int N = A.Length; 127 | 128 | //We will treat every A[K]*X+B as linear function 129 | LinearFunction[] functions = new LinearFunction[N]; 130 | for (int i = 0; i < N; i++) 131 | functions[i] = new LinearFunction(A[i], B[i]); 132 | 133 | //We sort the functions by slope and intercept 134 | Array.Sort(functions); 135 | 136 | //We get the upper and lower envelopes for the functions collection 137 | Envelope upperEnvelope = GetEnvelope(functions, EnvelopeKind.Upper); 138 | Envelope lowerEnvelope = GetEnvelope(functions, EnvelopeKind.Lower); 139 | 140 | int upperEnvelopePointIndex = 1; 141 | int lowerEnvelopePointIndex = 1; 142 | 143 | //We need to minimaze the distance between envelopes, we will start by going through upper envelope 144 | while (upperEnvelopePointIndex < upperEnvelope.Intersections.Length) 145 | { 146 | double distance = Double.MaxValue; 147 | 148 | //If current intersection in upper envelope lies before the current intersection in lower envelope (or there are no more intersections in lower envelope) 149 | if (upperEnvelope.Intersections[upperEnvelopePointIndex].X < lowerEnvelope.Intersections[lowerEnvelopePointIndex].X || lowerEnvelopePointIndex == lowerEnvelope.Intersections.Length - 1) 150 | { 151 | //The distance is equal to value at current intersection from upper envelope minus the value of current function from lower envelope in this point 152 | distance = upperEnvelope.Intersections[upperEnvelopePointIndex].Y - lowerEnvelope.Lines[lowerEnvelopePointIndex - 1].Evaluate(upperEnvelope.Intersections[upperEnvelopePointIndex].X); 153 | 154 | //We move to next intersection in upper envelope 155 | upperEnvelopePointIndex++; 156 | } 157 | //Otherwise (the current intersection in upper envelope lies further than current intersection in lower envelope and there are still other intersection in lower envelope) 158 | else 159 | { 160 | //The distance is equal to the value of current function from upper envelope in this point minus value at current intersection from lower envelope 161 | distance = upperEnvelope.Lines[upperEnvelopePointIndex - 1].Evaluate(lowerEnvelope.Intersections[lowerEnvelopePointIndex].X) - lowerEnvelope.Intersections[lowerEnvelopePointIndex].Y; 162 | 163 | //We move to next intersection in lower envelope 164 | lowerEnvelopePointIndex++; 165 | } 166 | 167 | //If new distance is smaller than result which we laready have 168 | if (distance < result) 169 | //We update the result 170 | result = distance; 171 | } 172 | } 173 | 174 | //Return the result 175 | return result; 176 | } 177 | #endregion 178 | 179 | #region Private methods 180 | private Envelope GetEnvelope(LinearFunction[] functions, EnvelopeKind kind) 181 | { 182 | Envelope envelope = null; 183 | 184 | if (functions != null && functions.Length > 0) 185 | { 186 | int firstFunctionIndex, lastFunctionIndex, functionsIterationStep; 187 | //While looking for upper envelope we will go from first to last function 188 | if (kind == EnvelopeKind.Upper) 189 | { 190 | firstFunctionIndex = 0; 191 | lastFunctionIndex = functions.Length - 1; 192 | functionsIterationStep = 1; 193 | } 194 | //While looking for lower envelope we will go from last to first function 195 | else 196 | { 197 | firstFunctionIndex = functions.Length - 1; 198 | lastFunctionIndex = 0; 199 | functionsIterationStep = -1; 200 | } 201 | 202 | List lines = new List(); 203 | List points = new List(); 204 | 205 | //We set the first line in the envelope 206 | LinearFunction previousLine = functions[firstFunctionIndex]; 207 | lines.Add(previousLine); 208 | 209 | //And calculate "theoretical" furthest to left point 210 | points.Add(new CartesianCoordinates(_leftBorder, previousLine.Evaluate(_leftBorder))); 211 | 212 | //We will look for intersection points between the functions 213 | for (int i = firstFunctionIndex + functionsIterationStep; (kind == EnvelopeKind.Upper && i <= lastFunctionIndex) || (kind == EnvelopeKind.Lower && i >= lastFunctionIndex); i += functionsIterationStep) 214 | { 215 | LinearFunction currentLine = functions[i]; 216 | //Because we have taken intercept into consideration while sorting we can skip parallel lines 217 | if (currentLine.Slope != previousLine.Slope) 218 | { 219 | //We add an intersection between previous and current line to the points collection 220 | points.Add(previousLine.Intersect(currentLine).Value); 221 | 222 | //If the latest intersection point lies before any of the previous one, we need to update those intersections to find the actual last intersection 223 | int lastIntersectionIndex = points.Count - 1; 224 | while (points[lastIntersectionIndex].X < points[lastIntersectionIndex - 1].X) 225 | { 226 | //Calculate the intersection between current on previous line 227 | points[lastIntersectionIndex - 1] = lines[lastIntersectionIndex - 2].Intersect(currentLine).Value; 228 | 229 | //Remove the no longer valid intersection and line 230 | points.RemoveAt(lastIntersectionIndex); 231 | lines.RemoveAt(lastIntersectionIndex - 1); 232 | 233 | lastIntersectionIndex--; 234 | } 235 | 236 | //We add current line to the lines collection 237 | previousLine = currentLine; 238 | lines.Add(previousLine); 239 | } 240 | } 241 | 242 | //We calculate "theoretical" furthest to right point 243 | points.Add(new CartesianCoordinates(_rightBorder, previousLine.Evaluate(_rightBorder))); 244 | 245 | envelope = new Envelope(lines.ToArray(), points.ToArray()); 246 | } 247 | 248 | return envelope; 249 | } 250 | #endregion 251 | } 252 | --------------------------------------------------------------------------------