/// <summary> /// Построение дерева штейнера алгоритмом Краскала /// </summary> /// <param name="points"> Опорные точки для построения </param> /// <returns> Список рёбер дерева </returns> public static dynamic Solve(List <T> points) { if (points.Count < 2) { return(null); } var pairs = new List <Tuple <Tuple <T, T>, double> >(); // составляем пары вершин for (int i = 0; i < points.Count; i++) { for (int j = i + 1; j < points.Count; j++) { pairs.Add(new Tuple <Tuple <T, T>, double>(new Tuple <T, T>(points[i], points[j]), Dist.GetOrthogonalDist(points[i], points[j]))); } } // упорядочиваем пары вершин по длинам рёбер pairs.Sort((t1, t2) => t1.Item2.CompareTo(t2.Item2)); var road = new List <Tuple <Tuple <T, T>, double> >(); double[,] matr = new double[points.Count, points.Count]; int[] parity = new int[points.Count]; for (int i = 0; i < pairs.Count; i++) { if (road.Count < 2) { int vert_1_ind = points.IndexOf(pairs[i].Item1.Item1); int vert_2_ind = points.IndexOf(pairs[i].Item1.Item2); road.Add(pairs[i]); parity[vert_1_ind]++; parity[vert_2_ind]++; matr[vert_1_ind, vert_2_ind] = matr[vert_2_ind, vert_1_ind] = pairs[i].Item2; } // проверка на цикличность else { int vert_1_ind = points.IndexOf(pairs[i].Item1.Item1); int vert_2_ind = points.IndexOf(pairs[i].Item1.Item2); parity[vert_1_ind]++; parity[vert_2_ind]++; matr[vert_1_ind, vert_2_ind] = matr[vert_2_ind, vert_1_ind] = pairs[i].Item2; // проверка на наличие цикла if (CyclesSearch(matr).Count > 0) { parity[vert_1_ind]--; parity[vert_2_ind]--; matr[vert_1_ind, vert_2_ind] = matr[vert_2_ind, vert_1_ind] = 0; } else { road.Add(pairs[i]); } } // проверка на связность графа bool[] visited = new bool[points.Count]; WidthSearch(points.IndexOf(pairs[0].Item1.Item1), matr, ref visited); if (parity.ToList().IndexOf(0) == -1 && visited.ToList().IndexOf(false) == -1) { break; } } var new_road = new List <Tuple <T, T> >(); for (int i = 0; i < road.Count; i++) { new_road.Add(new Tuple <T, T> (new Point2D(road[i].Item1.Item1.X, road[i].Item1.Item1.Y) as T, new Point2D(road[i].Item1.Item1.X, road[i].Item1.Item2.Y) as T)); new_road.Add(new Tuple <T, T> (new Point2D(road[i].Item1.Item1.X, road[i].Item1.Item2.Y) as T, new Point2D(road[i].Item1.Item2.X, road[i].Item1.Item2.Y) as T)); } // вырожденный случай, когда все точки находятся на одной линии if (points.FindAll(p => p.X == points[0].X).Count == points.Count || points.FindAll(p => p.Y == points[0].Y).Count == points.Count) { return(new_road); } for (int i = 0; i < new_road.Count; i++) { int ind_x = new_road.FindIndex(t => (!t.Item1.Equals(new_road[i].Item1) && !t.Item2.Equals(new_road[i].Item2)) && (t.Item1.Y == new_road[i].Item1.Y) && (t.Item2.Y == new_road[i].Item2.Y) && (t.Item1.Y == new_road[i].Item2.Y)); if (ind_x > -1) { if ((new_road[ind_x].Item1.X == new_road[i].Item1.X) && (new_road[ind_x].Item1.X <= new_road[i].Item2.X) && (new_road[ind_x].Item2.X <= new_road[i].Item2.X)) { new_road[i] = new Tuple <T, T> (new Point2D(new_road[ind_x].Item2.X, new_road[ind_x].Item2.Y) as T, new Point2D(new_road[i].Item2.X, new_road[i].Item2.Y) as T); } else if ((new_road[ind_x].Item2.X == new_road[i].Item2.X) && (new_road[ind_x].Item1.X <= new_road[i].Item2.X) && (new_road[ind_x].Item1.X <= new_road[i].Item1.X)) { new_road[i] = new Tuple <T, T> (new Point2D(new_road[ind_x].Item1.X, new_road[ind_x].Item1.Y) as T, new Point2D(new_road[i].Item2.X, new_road[i].Item2.Y) as T); } } else { int ind_y = new_road.FindIndex(t => (!t.Item1.Equals(new_road[i].Item1) && !t.Item2.Equals(new_road[i].Item2)) && (t.Item1.X == new_road[i].Item1.X) && (t.Item2.X == new_road[i].Item2.X) && (t.Item1.X == new_road[i].Item2.X)); if (ind_y > -1) { if ((new_road[ind_y].Item1.Y == new_road[i].Item1.Y) && (new_road[ind_y].Item1.Y <= new_road[i].Item2.Y) && (new_road[ind_y].Item2.Y <= new_road[i].Item2.Y)) { new_road[i] = new Tuple <T, T> (new Point2D(new_road[ind_y].Item2.X, new_road[ind_y].Item2.Y) as T, new Point2D(new_road[i].Item2.X, new_road[i].Item2.Y) as T); } else if ((new_road[ind_y].Item2.Y == new_road[i].Item2.Y) && (new_road[ind_y].Item1.Y <= new_road[i].Item2.Y) && (new_road[ind_y].Item1.Y <= new_road[i].Item1.Y)) { new_road[i] = new Tuple <T, T> (new Point2D(new_road[ind_y].Item1.X, new_road[ind_y].Item1.Y) as T, new Point2D(new_road[i].Item2.X, new_road[i].Item2.Y) as T); } } } } // разделяем все рёбра на линии, лежащие на одной оси var solution = new List <Tuple <T, T> >(); for (int i = 0; i < new_road.Count; i++) { if ((new_road[i].Item1.X == new_road[i].Item2.X) || (new_road[i].Item1.Y == new_road[i].Item2.Y)) { solution.Add(new_road[i]); } else { solution.Add(new Tuple <T, T>( new Point2D(new_road[i].Item1.X, new_road[i].Item1.Y) as T, new Point2D(new_road[i].Item1.X, new_road[i].Item2.Y) as T)); solution.Add(new Tuple <T, T>( new Point2D(new_road[i].Item1.X, new_road[i].Item2.Y) as T, new Point2D(new_road[i].Item2.X, new_road[i].Item2.Y) as T)); } } // отсортируем правильно все пары точек (чтобы первая точка была выше/правее второй) for (int i = 0; i < solution.Count; i++) { if (solution[i].Item1.X == solution[i].Item2.X) { if (solution[i].Item1.Y <= solution[i].Item2.Y) { solution.Insert(i, new Tuple <T, T>(solution[i].Item2, solution[i].Item1)); solution.RemoveAt(i + 1); } } else if (solution[i].Item1.Y == solution[i].Item2.Y) { if (solution[i].Item1.X <= solution[i].Item2.X) { solution.Insert(i, new Tuple <T, T>(solution[i].Item2, solution[i].Item1)); solution.RemoveAt(i + 1); } } } // разделяем накладывающиеся рёбра int k = 0; while (k < solution.Count) { if (solution[k].Item1.Equals(solution[k].Item2)) { solution.RemoveAt(k); } if (solution[k].Item1.X == solution[k].Item2.X) { int ind = solution.FindLastIndex(edge => ((edge.Item1.X == edge.Item2.X) && (edge.Item1.Y == solution[k].Item1.Y || edge.Item2.Y == solution[k].Item2.Y))); if (ind != -1 && ind != k) { if (solution[k].Item2.Equals(solution[ind].Item2)) { if (solution[k].Item1.Y < solution[ind].Item1.Y) { solution.Add(new Tuple <T, T>( new Point2D(solution[ind].Item1.X, solution[ind].Item1.Y) as T, new Point2D(solution[k].Item1.X, solution[k].Item1.Y) as T)); solution.RemoveAt(ind); k = -1; } else if (solution[k].Item1.Y > solution[ind].Item1.Y) { solution.Add(new Tuple <T, T>( new Point2D(solution[k].Item1.X, solution[k].Item1.Y) as T, new Point2D(solution[ind].Item1.X, solution[ind].Item1.Y) as T)); solution.RemoveAt(k); k = -1; } else { solution.RemoveAt(k); k = -1; } } else if (solution[k].Item1.Equals(solution[ind].Item1)) { if (solution[k].Item2.Y < solution[ind].Item2.Y) { solution.Add(new Tuple <T, T>( new Point2D(solution[ind].Item2.X, solution[ind].Item2.Y) as T, new Point2D(solution[k].Item2.X, solution[k].Item2.Y) as T)); solution.RemoveAt(k); k = -1; } else if (solution[k].Item2.Y > solution[ind].Item2.Y) { solution.Add(new Tuple <T, T>( new Point2D(solution[k].Item2.X, solution[k].Item2.Y) as T, new Point2D(solution[ind].Item2.X, solution[ind].Item2.Y) as T)); solution.RemoveAt(ind); k = -1; } else { solution.RemoveAt(k); k = -1; } } } } else if (solution[k].Item1.Y == solution[k].Item2.Y) { int ind = solution.FindLastIndex(edge => ((edge.Item1.Y == edge.Item2.Y) && (edge.Item1.X == solution[k].Item1.X || edge.Item2.X == solution[k].Item2.X))); if (ind != -1 && ind != k) { if (solution[k].Item2.Equals(solution[ind].Item2)) { if (solution[k].Item1.X < solution[ind].Item1.X) { solution.Add(new Tuple <T, T>( new Point2D(solution[ind].Item1.X, solution[ind].Item1.Y) as T, new Point2D(solution[k].Item1.X, solution[k].Item1.Y) as T)); solution.RemoveAt(ind); k = -1; } else if (solution[k].Item1.X > solution[ind].Item1.X) { solution.Add(new Tuple <T, T>( new Point2D(solution[k].Item1.X, solution[k].Item1.Y) as T, new Point2D(solution[ind].Item1.X, solution[ind].Item1.Y) as T)); solution.RemoveAt(k); k = -1; } else { solution.RemoveAt(k); k = -1; } } else if (solution[k].Item1.Equals(solution[ind].Item1)) { if (solution[k].Item2.X < solution[ind].Item2.X) { solution.Add(new Tuple <T, T>( new Point2D(solution[ind].Item2.X, solution[ind].Item2.Y) as T, new Point2D(solution[k].Item2.X, solution[k].Item2.Y) as T)); solution.RemoveAt(k); k = -1; } else if (solution[k].Item2.X > solution[ind].Item2.X) { solution.Add(new Tuple <T, T>( new Point2D(solution[k].Item2.X, solution[k].Item2.Y) as T, new Point2D(solution[ind].Item2.X, solution[ind].Item2.Y) as T)); solution.RemoveAt(ind); k = -1; } else { solution.RemoveAt(k); k = -1; } } } } k++; } return(solution); }