Smallest cost traversal of an array - c#

How do you compute the smallest cost traversal of an integer array using steps and jumps, while also counting the first and last element of the array? A step is moving to the next immediate value in the array e.g. array[currentIndex + 1], and a jump is moving two spots e.g. array[currentIndex + 2]. I have the following function which I want to return the minimum sum started, it adds the first and last elements to the sum, but I'm stuck on the middle values of the array.
An example of this would be {2, 10, 4, 14, 44, 28, 16, 18} -> 66
which would add indexes 0, 2, 3, 5, and 7.
====
public int Cost(int[] board)
{
int sum = board[0];
int index = 0;
while (index < board.Length)
{
//Add the final array value to the sum
if (index + 1 == board.length)
{
sum += board[index];
break;
}
//Add other values here
index++;
}
return sum;
}

You can try this:
public int Cost(int[] board)
{
int[] cost = new int[board.Length];
for (int i = 0; i < board.Length; i++) {
if (i == 0) {
cost[i] = board[0];
} else if (i == 1) {
cost[i] = board[1] + cost[0];
} else {
cost[i] = board[i] + Math.Min(cost[i - 1], cost[i - 2]);
}
}
return cost[board.Length - 1];
}

One possible solution:
public int Cost(int[] board, int step = 1)
{
if (board == null) return -1;
if (board.Length == 0) return 0;
// always add first and last index (if its not the first)
int sum = board[0];
if (board.Length > 1) sum += board[board.Length - 1];
// assumes step is > 0
for (int i = step; i < (board.Length - 1); i += step)
{
sum += board[i];
}
return sum;
}
This allows for step to be a parameter. Maybe now you want to step either 1 or 2 away from the start. Maybe later you want to step 5 spots away.

Related

How to remove two elements of collection at the same time? Task about cyclopes lenses

I cant solve this task.
There are N cyclopes and an array of N elements.
Every element is eyesight value of single cyclop.
Every cyclop needs a lens with a value K but he will be okay with
lens of value K+1 or K-1.
Cyclopes always buy lenses in pairs.
For example 5 cyclopes with eyesight values [1,-1,2,3,-3] will need to buy 3 pairs of lenses.
I need to write a program that will count minimal amount of lens pairs need.
I tried it like this
int cyclops = 4;
int[] cyclopsSightValues = { 1, 7, 4, 1 };
if (cyclops < 2) { return 1;}
List<int> list = cyclopsSightValues .ToList();
int matchCount = 0;
for (int i = 0; i < list.Count; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (list[i] == list[j] ||
list[i] + 1 == list[j] ||
list[i] + 2 == list[j] ||
list[i] - 1 == list[j] ||
list[i] - 2 == list[j])
{
int valueToRemove1 = list[i];
int valueToRemove2 = list[j];
list.Remove(valueToRemove1);
list.Remove(valueToRemove2);
matchCount++;
continue;
}
}
}
return matchCount + (cyclops-matchCount*2);
I think i need to find matching eyesights and remove them from list, but the result always comes out less then the correct one by 1.
Maybe my logic is wrong altogether?
Any help would be much appreciated.
Look, if two cyclops have eyesight difference 2 or less by absolute value they can buy lenses which fit both of them, e.g.
3 and 1 can buy pair of 2 lenses. Let's try to use greedy approach now: order cyclops by their eye sights and try
to use spare lenses as frequent as we could:
1, -1, 2, 3, -3 -> -3, -1, 1, 2, 3
-3 v -1, 1 v 2, 3
can use
-2 1
So far so good all we have to do is to sort and scan:
private static int Solve(int[] cyclopsSightValues) {
Array.Sort(cyclopsSightValues);
int result = 0;
bool hasSpare = false;
for (int i = 0; i < cyclopsSightValues.Length; ++i)
if (hasSpare && cyclopsSightValues[i - 1] + 2 >= cyclopsSightValues[i])
hasSpare = false; // We use spare lense from the previous cyclope
else {
// we have to buy a pair, and now we have a spare lense
hasSpare = true;
result += 1;
}
return result;
}
Demo:
int[][] tests = {
new[] { 1, -1, 2, 3, -3 },
new int[] { },
new[] { 1, 1, 1, 1 },
};
string report = string.Join(Environment.NewLine, tests
.Select(item => $"[{string.Join(", ", item)}] => {Solve(item)}"));
Console.Write(report);
Output:
[1, -1, 2, 3, -3] => 3
[] => 0
[1, 1, 1, 1] => 2
Please, fiddle yourself
Untested, but should more or less work:
public static IEnumerable<int> LensesToBuy(IEnumerable<int> cyclopsVisions)
{
int? buf = null;
var e = cyclopsVisions.OrderBy(v => v).GetEnumerator();
while(e.MoveNext())
{
if (!buf.HasValue)
{
buf = e.Current;
}
else if (Math.Abs(buf.Value - e.Current) > 2)
{ // cached value not compatible
yield return buf.Value;
buf = e.Current;
}
else
{ // cached value is compatible
if (buf.Value == e.Current) yield return buf.Value;
if (buf.Value > e.Current) yield return buf.Value - 1;
if (buf.Value < e.Current) yield return buf.Value + 1;
buf = null;
}
}
if (buf.HasValue) yield return buf.Value;
}
Call it like this:
int[] cyclopsSightValues = { 1, 7, 4, 1 };
var result = LensesToBuy(cyclopsSightValues); //okay to pass array
Console.WriteLine(result.Count());

C# checking if next value in array is null

This code cycles thru an array of integers, adding the int at the current index with the int at the next index (i + 1) and then finding their average (dividing by 2). This double is then appended to an array of doubles. When I get to the end of the array however, since there is no value beyond the last value, an error occurs. I thought I could just check to see if the value was null but the error is occurring beforehand. Error occurs at line 15 "if (numbers[i + 1] != null)."
using System;
using System.Collections;
using System.Linq;
public class Test
{
public static double[] Averages(int[] numbers)
{
double[] averageArray = new double[0];
for (int i = 0; i < numbers.Length; i++)
{
double sum = 0;
double avg = 0;
if (numbers[i + 1] != null)
{
sum += numbers[i] + numbers[i + 1];
avg = sum / 2;
}
averageArray = averageArray.Append(avg).ToArray();
}
return averageArray;
}
public static void Main()
{
int[] testArray1 = { 2, 2, 2, 2 };
Console.WriteLine(Averages(testArray1));
}
}
Ok, so, couple of things that need to be considered with your code. How you have it right now will throw an out of range exception.
You should never be able to have a null object in your int array so thats not a check you need to do.
If you need it to accept nulls, you need to declare it as int?[]
The way you have your code now, the output \ return value is an array of 5 values, when you have 4 going in
2, 2, 2, 1, 0
void Main()
{
int[] testArray1 = { 2, 2, 2, 2, };
Console.WriteLine(Averages(testArray1));
}
public static double[] Averages(int[] numbers)
{
double[] averageArray = new double[0];
for (int i = 0; i <= numbers.Length; i++)
{
double sum = 0;
double avg = 0; // This will cause a last object to be added to your array
if (i == numbers.Length - 1) // This adds the last item, this also needs to have order or preference as this would also then cause a Index Out of Range Exception
{
sum += numbers[i];
}
else if(i <= numbers.Length -1) // This adds all items that are not the last item in the array
{
sum += numbers[i] + numbers[i + 1];
}
avg = sum / 2;
averageArray = averageArray.Append(avg).ToArray();
}
return averageArray;
}

C# problems getting results from List<T> BinarySearch with duplicates in the first and last position

I'm playing around with different implementations for the most simple of the LeetCode problems TwoSums. I have various other ways working fine using methods like indexof, Dictionary searches, brute force, etc... and my best so far is better than 98.67% using List.IndexOf
I'm trying to implement a version with BinarySearch for comparison. It passes most tests but seems to fail when needing to sum duplicates in the first and last position and the list length is gt 2. I'm sure there are other fail conditions but I can't get past this one.
When stepping through the code it returns a negative number instead of the index when the last and first are the same value despite starting at i+1 index location.
I have to be missing something obvious but I'm just not seeing it for some reason. Maybe I'm just misunderstanding how BinarySearch works with index and length.
These pass:
{ 0, 2, 3 } target 3
result: {0, 2}
{ 3, 3 } target 6
result: { 0, 1 }
{ 0, 2, 0, 3 } target 0
result: { 0, 2 }
These fail:
{ 0, 2, 0 } target 0
expected: { 0, 2 }
result: null
{ 1, 4, 1 } target 2
expected: { 0, 2 }
result: null
The code sample is verbose for now while I'm working through the issues. I'll minimize it later.
offset is used to start the search at an index higher than i
subsetLength is used to keep the search length from going outside the bounds
The n > i is just a sanity check to make sure n is a higher index value than i before returning a valid result
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}
Yes, you have a special case target / 2 + target / 2 == target when you check for two items each of which are target / 2:
public int[] TwoSum(int[] nums, int target) {
// Comment out if nums is already sorted
Array.Sort(nums);
for (int i = 0; i < nums.Length; ++i) {
int item = nums[i];
int toFind = target - item;
if (toFind < nums[i])
break;
int index = Array.BinarySearch(nums, toFind);
if (index >= 0) {
if (toFind == item) {
// Special case: two equal numbers: target / 2 + target / 2 = target
if (i < nums.Length - 1 && nums[i] == nums[i + 1])
return new int[] { i, i + 1 };
break;
}
else
return new int[] { i, index };
}
}
return new int[] { -1, -1 };
}
Just sort the list before searching it:
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
numList.Sort();
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}

C#, one-dimension wrapping array

I have a one-dimensional array, and I need to run operations on it based on the adjacents cells of every cell.
For instance:
To run the operations on the first cell, I'll need to access the last cell and the second.
The second cell, I'll need to access the first cell and the third cell.
The last cell, I'll need to access the first cell and the one before the last cell.
My code so far is:
public static int[] firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
if (numberArray[numberArray.Length - 1 - i] == numberArray[i] + 1
&& numberArray[i + 1] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
}
return numberArray;
}
But the problem with my approach is that it would only work for the first cell, as it would take the last cell and the second cell correctly, but after that, everything would fall apart. I don't like posting without a lot of code but I have no clue how to follow this up.
I am trying to achieve the following:
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
Just keep it simple and use variables to calculate the left and right cell indices. Inside your for loop you can do this...
var leftCell = i - 1;
if (leftCell < 0)
{
leftCell = numberArray.Length - 1; // Wrap around to the end...
}
var rightCell = i + 1;
if (rightCell > numberArray.Length - 1)
{
rightCell = 0; // Wrap back around to the beginning...
}
// Now you can update your original code to use these computed indices...
if (numberArray[leftCell] == numberArray[i] + 1
&& numberArray[rightCell] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
Try this out:
var len = numberArray.Length;
for (int i = 0; i < len; i++)
{
var leftIndex = (i - 1 + len) % len;
var rightIndex = (i + 1) % len;
// do your stuff with numberArray[leftIndex] and numberArray[rightIndex]
}
% is mod operator. % len allows you to stay in range 0..len-1, so you can walk through array as if it has become 'cyclic'
From your comments.
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
I would create a new array, populate it with the values of the existing array and then change the values of the new array according to the results of the value in the existing array.
Edit as Op is getting wrong values
I suspect you may not be copying the array correctly instead:
Existing Array array // The array you are passing in as parameter.
Declare a new empty array:
int[] newArray;
int size = array.length;
for(int i =1; i<size-1;i++){
if(array[i-1]==array[i+1])){
newArray[i]=array[i]+1;
}
else{
newArray[i]=array[i];
}
}
if(array[size-1]==array[0]){
newArray[size]= array[size]+1;
}
else{
newArray[i]=array[i];
}
if(array [size]==array[1]){
newArray[0]= array[0]+1;
}
else{
newArray[i]=array[i];
}
if there is a limit to the number and it reverts to zero after 2, then just do a simple if test for that.
int[] arr = new int[] { 1, 2, 3, 4, 5 };
var triples = arr.Select((n, i) =>
{
if (i == 0)
return Tuple.Create(arr[arr.Length - 1], arr[0], arr[1]);
else if (i == arr.Length - 1)
return Tuple.Create(arr[i - 1], arr[i], arr[0]);
else
return Tuple.Create(arr[i - 1], arr[i], arr[i + 1]);
});
foreach (var triple in triples)
{
Console.WriteLine(triple.Item1 + " " + triple.Item2 + " " + triple.Item3);
}
public static void firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
int? prevElement = i == 0
? numberArray[numberArray.Length-1]
: numberArray[i - 1];
int? nextElement = i == numberArray.Length -1
? numberArray[0]
: numberArray[i + 1];
Console.WriteLine(
String.Format("Prev: {0}; Current: {1}; Next: {2}",
prevElement,
numberArray[i],
nextElement)
);
}
}
And then calling firstRule(new int[]{ 1, 2, 3 }); prints:
Prev: 3; Current: 1; Next: 2
Prev: 1; Current: 2; Next: 3
Prev: 2; Current: 3; Next: 1
OPTION 1
assign regardless
public static int[] firstRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
numberArray[i] = (numberArray[left] == numberArray[right]) ? numberArray[i] + 1 : numberArray[i]; //always peforms an assignment;
}
return numberArray;
}
OPTION 2
conditionally assign
public static int[] secondRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
if (numberArray[left] == numberArray[right])
{
numberArray[i]++;
}
}
return numberArray;
}
OPTION 3
left and right are only used 1 time in each iteration.. so why bother assigning them to a variable???...
public static int[] thirdRule(int[] numberArray)
{
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
if (numberArray[(i == 0) ? max : i - 1] == numberArray[(i == max) ? 0 : i + 1])
{
numberArray[i]++; // what happens if numberArray[i] is 3, should it become 4 or 0?
}
}
return numberArray;
}
OPTION 4 (UNSAFE)
unsafe - fixed - pointers
public static int[] fourthRule(int[] numberArray)
{
unsafe {
int* pointer, right, left;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
fixed (int* p1 = &numberArray[0], p2 = &numberArray[i], p3 = &numberArray[max])
{
pointer = p2;
if (i == 0)
{
left = p3;
right = pointer;
right++;
}
else if (i == max)
{
left = pointer;
left--;
right = p1;
}
else
{
left = pointer;
left--;
right = pointer;
right++;
}
if (*right == *left) {
*pointer = *pointer + 1;
}
}
}
}
return numberArray;
}
Recently came up against this myself and found this to be a solid method.
`
int length = numberArray.Length;
for (int i = 0; i < length; ++i)
{
int iMinus = (((i - 1) % length) + length) % length;
int iPlus = (((i + 1) % length) + length) % length;
}`
Something like this should work. It determines the appropriate cells for the operation in each loop and executes the operation. You didn't state what that operation was so you need to fill in the DoYourOperation method.
public static int[] processArray(int[] numberArray)
{
for (int i= 0; i < numberArray.Length; i++)
{
int firstCell;
int secondCell;
//Check if first cell
if(i == 0)
{
firstCell = numberArray[numberArray.length-1]; //Last cell
secondCell = numberArray[i++]; //Next cell
}
//Check if last cell
else if(i == numberArray.Length - 1)
{
firstCell = numberArray[i--]; //Cell before last one
secondCell = numberArray[0]; //First cell
}
else
{
firstCell = numberArray[i--];
secondCell = numberArray[i++];
}
DoYourOperation(firstCell, secondCell);
}
}

Counting alternating numbers in an array

Given an array of integers...
var numbers = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
I need to determine a the maximum sequence of numbers that alternate up then down or down then up.
Not sure the best way to approach this, the process psuedo wise strikes me as simple but materializing code for it is evading me.
The key is the fact we are looking for max sequence, so while the above numbers could be interpreted in many ways, like a sequence of seven up-down-up and seven down-up-down the important fact is starting with the first number there is a down-up-down sequence that is 14 long.
Also I should not that we count the first item, 121 is a sequence of length 3, one could argue the sequence doesn't begin until the second digit but lets not split hairs.
This seems to work, it assumes that the length of numbers is greater than 4 (that case should be trivial anyways):
var numbers = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
int count = 2, max = 0;
for (int i = 1; i < numbers.Length - 1; i++)
{
if ((numbers[i - 1] < numbers[i] && numbers[i + 1] < numbers[i]) ||
(numbers[i - 1] > numbers[i] && numbers[i + 1] > numbers[i]))
{
count++;
max = Math.Max(count, max);
}
else if ((numbers[i - 1] < numbers[i]) || (numbers[i - 1] > numbers[i])
|| ((numbers[i] < numbers[i + 1]) || (numbers[i] > numbers[i + 1])))
{
max = Math.Max(max, 2);
count = 2;
}
}
Console.WriteLine(max); // 14
Here's how I thought of it
First, you need to know whether you're starting high or starting low. eg: 1-2-1 or 2-1-2. You might not even have an alternating pair.
Then, you consider each number afterwards to see if it belongs in the sequence, taking into consideration the current direction.
Everytime the sequence breaks, you need to start again by checking the direction.
I am not sure if it is possible that out of the numbers you have already seen, picking a different starting number can POSSIBLY generate a longer sequence. Maybe there is a theorem that shows it is not possible; maybe it is obvious and I am over-thinking. But I don't think it is possible since the reason why a sequence is broken is because you have two high's or two low's and there is no way around this.
I assumed the following cases
{} - no elements, returns 0
{1} - single element, returns 0
{1, 1, 1} - no alternating sequence, returns 0
No restriction on the input beyond what C# expects. It could probably be condensed. Not sure if there is a way to capture the direction-change logic without explicitly keeping track of the direction.
static int max_alternate(int[] numbers)
{
int maxCount = 0;
int count = 0;
int dir = 0; // whether we're going up or down
for (int j = 1; j < numbers.Length; j++)
{
// don't know direction yet
if (dir == 0)
{
if (numbers[j] > numbers[j-1])
{
count += 2; // include first number
dir = 1; // start low, up to high
}
else if (numbers[j] < numbers[j-1])
{
count += 2;
dir = -1; // start high, down to low
}
}
else
{
if (dir == -1 && numbers[j] > numbers[j-1])
{
count += 1;
dir = 1; // up to high
}
else if (dir == 1 && numbers[j] < numbers[j-1])
{
count += 1;
dir = -1; // down to low
}
else
{
// sequence broken
if (count > maxCount)
{
maxCount = count;
}
count = 0;
dir = 0;
}
}
}
// final check after loop is done
if (count > maxCount)
{
maxCount = count;
}
return maxCount;
}
And some test cases with results based on my understanding of the question and some assumptions.
static void Main(string[] args)
{
int[] nums = { 1}; // base case == 0
int[] nums2 = { 2, 1 }; // even case == 2
int[] nums3 = { 1, 2, 1 }; // odd case == 3
int[] nums4 = { 2, 1, 2 }; // flipped starting == 3
int[] nums5 = { 2, 1, 2, 2, 1, 2, 1 }; // broken seqeuence == 4
int[] nums6 = { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1 }; // long sequence == 14
Console.WriteLine(max_alternate(nums));
Console.WriteLine(max_alternate(nums2));
Console.WriteLine(max_alternate(nums3));
Console.WriteLine(max_alternate(nums4));
Console.WriteLine(max_alternate(nums5));
Console.WriteLine(max_alternate(nums6));
Console.ReadLine();
}
I'm not from a pc with a compiler right now, so I just give a try:
int max = 0;
int aux =0;
for(int i = 2 ; i < length; ++i)
{
if (!((numbers[i - 2] > numbers[i - 1] && numbers[i - 1] < numbers[i]) ||
numbers[i - 2] < numbers[i - 1] && numbers[i - 1] > numbers[i]))
{
aux = i - 2;
}
max = Math.Max(i - aux,max);
}
if (max > 0 && aux >0)
++max;
Note: should works for sequence of at least 3 elements.
There are probably a lot of ways to approach this, but here is one option:
var numbers = new int[] { 7,1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1 };
int maxCount = 0;
for (int j = 0; j+1 < numbers.Length; j++)
{
int count = 0;
if (numbers[j] < numbers[j+1])
{
count += 2;
for (int i = j+2; i+1 < numbers.Length; i += 2)
{
if (numbers[i] < numbers[i + 1] )
{
count += 2;
}
else
{
break;
}
}
}
if (maxCount < count)
{
maxCount = count;
}
}
Console.WriteLine(maxCount);
Console.ReadLine();
This solution assumes that you want a sequence of the same two alternating numbers. If that's not a requirement you could alter the second if.
Now that it's written out, it looks more complex than I had imagined in my head... Maybe someone else can come up with a better solution.
Assumes at least 2 elements.
int max = 1;
bool expectGreaterThanNext = (numbers[0] > numbers[1]);
int count = 1;
for (var i = 0; i < numbers.Length - 1; i++)
{
if (numbers[i] == numbers[i + 1] || expectGreaterThanNext && numbers[i] < numbers[i + 1])
{
count = 1;
expectGreaterThanNext = (i != numbers.Length - 1) && !(numbers[i] > numbers[i+1]);
continue;
}
count++;
expectGreaterThanNext = !expectGreaterThanNext;
max = Math.Max(count, max);
}
This works for any integers, it tracks low-hi-low and hi-low-hi just like you asked.
int numbers[] = new int[] { 1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,1 };
int count = 0;
int updownup = 0;
int downupdown = 0;
for(int x = 0;x<=numbers.Length;x++)
{
if(x<numbers.Length - 2)
{
if(numbers[x]<numbers[x+1])
{
if(numbers[x+1]>numbers[x+2])
{
downupdown++;
}
}
}
}
count = 0;
for(x=0;x<=numbers.Length;x++)
{
if(x<numbers.Length - 2)
{
if(numbers[x]>numbers[x+1]
{
if(numbers[x+1]<numbers[x+2])
{
updownup++;
}
}
}
}

Categories