First of all, the coordinates can be translated/projected in such a way that the rectangle size is 1x1 and the orientation is 0° (aligned with the X/Y axis). So I will assume that situation.
Then you could proceed as follows:
- Sort the points by their x-coordinate
- Create a directed graph as follows:
- For each point a, take the points in the x-interval [ax, ax+1], excluding a itself
- Exclude from this list those points whose y-coordinate is not in the range [ay-1, ay+1]
- Sort these points by y-coordinate
- Put these points in the adjacency list for point a.
- Mark all points as unvisited
- If there are no unvisited points, return 0 to indicate that no (more) rectangles were needed.
- Take the next unvisited point a from the sorted list of points: mark a as visited.
- If a has no neighbors, then increase the rectangle count and repeat from step 4
- Slide a rectangle, with its left x-coordinate always equal to ax, over the neighbors, such that:
- the first chosen rectangle has its low-y-coordinate common with the first unvisited neighbor point
- maintain a list L of neighbors that get visited (only if they were not already marked as visited) by getting covered by this rectangle
- all next rectangle positions each gain one unvisited point at their high-y-coordinate end.
- The points in L that get uncovered at the low-y side of the sliding rectangle, are removed from L and marked unvisited again
- Each time a new rectangle position is identified, a recursive (DFS) call is made that starts at step 4.
- The DFS call will return the number of rectangles that were still needed to cover all the remaining, unvisited points
- Keep the minimum value returned from all these recursive calls, and add 1 (for the current rectangle)
- NB: The recursion tree can be pruned by passing the current minimum result as cut-off point, so to avoid searches that increase the number of rectangles too much.
- When all rectangle positions have been processed, mark any points still in L as unvisited.
- Return the minimum count of rectangles found.
This could still be a very expensive algorithm, as the search tree can easily become very wide.
A potential improvement could be the use of BFS instead of DFS, using a priority queue (e.g. Min Heap). The number to minimise would be the number of rectangles already used (i.e. the cost so far) plus the x-coordinate difference between the right most (unvisited) point and left-most unvisited point, rounded upwards (i.e. a lower bound of the cost still ahead). This is an A* algorithm, so you can stop the search as soon as you get a situation where all points were covered (visited).
The downside of this BFS method is the memory use and management, since each state (in the priority queue) includes a set of visited points.