2

I'm trying to create a game but I am having some difficulties in coming up with a suitable algorithm for my problem. I have elements from 1 to n and I am trying to cover all of the elements using the minimum amount of elements as possible. Each element can cover an amount of elements to the left (x direction) and an amount of elements to the right direction (y direction) I'm trying to get all of them connected, meaning that if i pick one element z, each pair of adjacent elements i and i+1 should be covered from z. The goal is to find the minimum amount of elements in order to cover the entire 1 to n elements.

This is an example:
If n= 7 [so i have a list with n elements, it can be mountaints, towers, etc.]

x= 0 1 1 2 1 1 1 [how many elements you can cover to the left]
y= 1 2 1 2 1 1 0 [how many elements you can cover to the right]

#as you can see, in element 2, you can cover 1 element to the left, and 2 elements to the right(look at index 1 if counting from 0 in the x list and y list)

To clarify, the reason it is 0 in the first x element, is because you can cover 0 elements to the left, and 0 in the last y element because you can cover 0 elements to the right. So from each element i, you can cover everything from i-xi and i+yi.

Answer: we could pick elements 2, 4 and 7 to cover all of the 7 elements (there are many other combinations). So the minimum would be 3 which is the right answer for this input

The reason why I dont pick element 2 and 6,(one might think they cover all of them) is because that would leave a gap between element 4 and 5 where they are not connected. So the minimum is not 2, but 3

How can I go about to solve this?

I have looked at interval scheduling, as well as vertex cover, but vertex cover would not solve the issue that you cant cover the same amount of elements both ways, for example, element 4 can cover ut to element 6 in my example, but not vice versa. I have looked at dynamic programming and greedy algorithms, but they do not seem to solve the connecting issue, and would give the output of 2 and not 3, so Im a bit lost in how to tackle this. I am hoping that there is someone out there that can help me on the way, any advice, recommendations, youtube clips, pages and such are highly appreciated. Thank you in advance. If there are any clarifications needed let me know.

1 Answers1

0

Be greedy.

Of all the elements that cover the leftmost square, pick the one that extends furthest to the right. Then repeat for the leftmost uncovered square until all squares are covered.

x= 0 1 1 2 1 1 1
y= 1 2 1 2 1 1 0

1. choose index 1, covering 0-3, and leaving 4-6 uncovered.
2. choose index 3 or 4, covering 4-5.
3. choose index 5 or 6, covering 6.

Ruby code:

# ranges is an array of ranges covered by the elements. The size of ranges is the set of indices we need to cover
def f(ranges)
  rightmost_covered_index = -1
  ans = []
  while rightmost_covered_index < ranges.length - 1
    best_range = nil
    best_right_index = -1
    ranges.each do |range|
      if range[0] <= rightmost_covered_index + 1 && range[1] > best_right_index
        best_right_index = range[1]
        best_range = range
      end
    end
    if best_right_index == -1
      puts "no solution"
      return
    else
      ans.append(best_range)
      rightmost_covered_index = best_range[1]
    end
  end
  return ans
end

Sample results:

> f([[0,1],[0,3],[1,3],[1,5],[3,5],[4,6],[0,6]])
=> [[0, 6]]

> f([[0,1],[0,3], [1,3],[1,5],[3,5],[4,6],[5,6]])
=> [[0, 3], [4, 6]]
Dave
  • 7,460
  • 3
  • 26
  • 39
  • Lets say i merge the two lists to create an upper and lower bound we will get (0,1) (0,3) (1,3) (1,5) (3,5) (4,6) (5,6) meaning that index 0 can cover from 0 to 1, index 1 can cover from 0 to 3 etc. if i start from the left, and pick each square that covers a new element, in this case it would be correct, because it would pick 1,3 and 5. but what if i have the edgecase (0,1) (0,3) (1,3) (1,5) (3,5) (4,6) (0,6) then i would start from the left, and pick 1,3,5, when in fact, the right answer would be to only pick 6 (since it covers all of the elements) – Rizzu Rizzman May 26 '23 at 22:12
  • @RizzuRizzman You don't do that. Either look at all elts each time or sort them. If this is a puzzle for humans, algorithmic complexity shouldn't matter. – Dave May 26 '23 at 22:49
  • Im not sure I quite understand, since the algorithm in this case (0,1) (0,3) (1,3) (1,5) (3,5) (4,6) (0,6) , the algorithm would output the answer as minimum 3, when the answer will be 1, so the algorithm will not always output the minimum amount of elements that can cover everything, I was thinking if I could solve it using recursion, but I was hoping it was solveable using a method where you only use forloops – Rizzu Rizzman May 27 '23 at 15:36
  • @RizzuRizzman For the input you gave, in the first step you pick (0.6) because that's the element that covers 0 and extends furthest to the right, then you're done. – Dave May 27 '23 at 16:12
  • @RizzuRizzman I added code (Ruby) to clarify. You could make this more efficient by sorting by the left endpoint of the ranges, but as I said earlier, if this is for puzzles that humans are meant to solve, there's no point. – Dave May 27 '23 at 16:25
  • Right, Im understanding more of what you mean now, thank you! It would pick (0,6) first since it is one of the leftmost squares and the only one that extends furthest to the right. Yes it is true that there is no point if it is for humans, out of curiosity, I am trying to solve it fast and efficient, Im currently starting to look at fast algorithms(still learning). Im thinking, if I would sort the ranges based on the rightmost index and using binary search instead of a linear search, the code would be quicker? This method of search looks similat to interval schedueling, am I correct? – Rizzu Rizzman May 27 '23 at 17:15
  • Actually since you have as many ranges as cells, you can bucket sort to solve this in linear time. Make an array where cell i stores the largest right--end of ranges whose left end is i. I'm on my phone now so can't add details, may do so later if you like. – Dave May 27 '23 at 20:53