Пример #1
0
        /// <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);
        }