Warm tip: This article is reproduced from stackoverflow.com, please click
algorithm language-agnostic path-finding robotics

Connecting a set of points with horizontally aligned polylines without them crossing

发布于 2020-06-24 15:53:13

I have been trying to solve this problem for a few days now, but I can't find a good solution.

I have a set of 2D points where all values are integers. No points are identical. I want to draw polylines/paths/whatever through all the points with a few restrictions:

1: A line should always move in the positive x-direction. p1.x < p2.x < ...

2: Lines may never cross each other.

3: All polylines needs to begin at x = 0 and end at x-max.

4: It should use as few polylines as possible (or a number defined by me).

I have attached an image of a sample set of points. And a hand made solution I drew with a pencil and ruler.

It's trivial to find a solution by hand, but I have no idea how to describe my process in logical terms. I don't need an optimal solution (whatever that means). It doesn't need to be fast.

Point set (Disregard colors)

Connected Points

My current solution is to step through the set along the x-axis and then try all viable combinations and choosing the one with the lowest total vertical movement. This works in some cases but not all. And it seems to over-complicate the problem.

My next idea is to do a brute force approach with backtracking when collisions occour. But that also seems a bit much.

For anyone wondering the points are actually notes on sheet music. The x-axis is time and the y-axis is pitch. The polylines represent the movement of robotic fingers playing a piano.

Questioner
Hermasetas
Viewed
26
CodeChef 2020-04-06 10:13

We will find a solution which uses the minimum number of robotic fingers (least number of polylines). The trick is to consider your input as a Partially ordered set (or poset). Each point in your input is an element of the poset, and with the relation (p1.x, p1.y) < (p2.x , p2.y) if and only if p1.x < p2.x. This basically means that two points which have the same x-coordinate are incomparable with each other.

For now, let us forget this constraint: "Lines may never cross each other". We'll get back to it at the end.

What you are looking for, is a partition of this poset into chains. This is done using Dilworth's Theorem. It should be clear that if there are 5 points with the same x-coordinate, then we need at least 5 different polylines. What Dilworth's says is that if there is no x-coordinate with more than 5 points on it, then we can get 5 polylines (chains) which cover all the points. And it also gives us a way to find these polylines, which I'm summarizing here:

You just create a bipartite graph G = (U,V,E) where U = V = Set of all input points and where (u,v) is an edge in G if u.x < v.x. Then find a maximum matching, M, in this graph, and consider the set of polylines formed by including u and v in the same polyline whenever there is an edge (u,v) in M.

The only issue now, is that some of these polylines could cross each other. We'll see how to fix that:

First, let us assume that there are only two polylines, L1 and L2. You find the first instance (minimum x-coordinate) of their crossing. Suppose the two line segments which cross each other are AB and CD:

Crossing

We delete AB and CD and instead add AD and CB:

Crossing delayed

The polylines still cross each other, but their point of crossing has been delayed. So we can keep repeating this process until there is no crossing left. This takes at most n iterations. Thus we know how to 'untangle' two polylines.

[The edge case of B lying on the segment CD is also handled in the exact same way]

Now, suppose we have k different polylines which the maximum matching has given us: L1, L2, ..., Lk. WLOG, let us assume that at x = 0, L1's y-coordinate is lower than L2's y-coordinate, which is lower than L3's and so on.

Take L1 and find the first time that it crosses with any other polyline. At that crossing, applying the swapping operation as above. Keep repeating this, until L1 does not cross with any other polyline. Now, L1 is at the 'bottom', and doesn't cross with any other line. We now output L1 as one of the final polylines, and delete it from our algo. We then repeat the same process with L2, and after outputting it, delete it, and repeat with L3, and so on.