zoukankan      html  css  js  c++  java
  • 轮廓问题/Outline Problem

    ---------------------------------------------------

    //已发布改进后的轮廓问题算法:http://www.cnblogs.com/andyzeng/p/3683498.html

    ---------------------------------------------------

    对于城市中几座建筑外形,给出这些建筑的二维轮廓。
    每个建筑的输入为 L H R,其中L,R分辨代表建筑在水平线上的左右x坐标,H表示建筑的高度。在图形上,一个建筑就是一个条形图。
    建筑随机输入,请设计一个算法能够输出所有建筑所产生的轮廓线。

    输入建筑的文件内容格式为,粗体格式为建筑高度:

    10 110 50
    20 60 70
    30 130 90
    120 70 160
    140 30 250
    190 180 220
    230 130 290
    240 40 280

    输入轮廓的文件内容格式为,粗体格式为轮廓高度:10 110 30 130 90 0 120 70 160 30 190 180 220 30 230 130 290 0

    解答如下:

    给出建筑类和轮廓线上点的定义:

      public class Building
        {
            public Building(int x1, int h, int x2)
            {
                X1 = x1;
                H = h;
                X2 = x2;
            }
            public int X1 { get; set; }
            public int H { get; set; }
            public int X2 { get; set; }   
        }
    
        //轮廓线上点的定义
        public class Point
        {
            public Point(double x, int y)
            {
                X = x;
                Y = y;
            }
            public double X { get; set; }
            public int Y { get; set; }
        }

    初始化输入,按照一定要求随机生成建筑

     public static Building[] initBuildings(int buildCount, int leftLimitInclusive, int maxHeightLimitInclusive, int rightLimitInclusive)
            {
                Building[] buildings = new Building[buildCount];
                Random rndRange = new Random(DateTime.Now.Millisecond);
                Random rndHeight = new Random(DateTime.Now.Millisecond);
                for (int i = 0; i < buildCount; i++)
                {
                    int l = rndRange.Next(leftLimitInclusive, rightLimitInclusive);
                    int r = rndRange.Next(l + 1, rightLimitInclusive + 1);
                    int h = rndHeight.Next(1, maxHeightLimitInclusive + 1);
                    Building bld = new Building(l, h, r);
                    buildings[i] = bld;
                }
                return buildings;
            }

    运用分治法divide and conquer 来进行建筑两两合并,然后将产生的轮廓线再进行两两合并

     public static List<Point> MergeBuildings(Building[] blds, int leftIndex, int rightIndex)
            {
                if (rightIndex - leftIndex <= 1)//one or two buildings
                {
                    return mergeTwoBuildingsImpl(blds[leftIndex], blds[rightIndex]);
                }
                else
                {
                    int middle = (rightIndex + leftIndex) / 2;
                    List<Point> firstOutlines = MergeBuildings(blds, leftIndex, middle);
                    List<Point> secondOutlines = MergeBuildings(blds, middle + 1, rightIndex);
                    return mergeTwoOutLinesImpl(firstOutlines, secondOutlines);
                }
            }

    其中建筑合并的时候考虑两个建筑的相对横坐标(L和R)和纵坐标(高H)的关系。

      private static List<Point> mergeTwoBuildingsImpl(Building first, Building second)
            {
                Building left, right;
                if (Math.Min(first.X1, second.X1) == second.X1)
                {
                    left = second;
                    right = first;
                }
                else
                {
                    left = first;
                    right = second;
                }
                List<Point> points = new List<Point>();
                #region Lx1<Lx2<=Rx1<Rx2
                if (left.X2 <= right.X1)
                {
                    if (left.X2 < right.X1)
                    {
                        points.AddRange(
                            new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H),
                                new Point(left.X2,left.H),
                                new Point(left.X2,0),
                                new Point(right.X1,0),
                                new Point(right.X1,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                    }
                    else//==
                    {
                        if (left.H == right.H)
                        {
                            points.AddRange(
                                new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H),                            
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                        else
                        {
                            points.AddRange(
                                new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H),
                                new Point(left.X2,left.H), 
                                new Point(right.X1,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                    }
                }
                #endregion
                #region Lx1<=Rx1<Lx2<=Rx2
                if (left.X1 <= right.X1 && right.X1 < left.X2 && left.X2 <= right.X2)
                {
                    if (left.X1 < right.X1 && left.X2 < right.X2)
                    {
                        if (left.H < right.H)
                        {
                            points.AddRange(
                                   new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(right.X1,left.H),
                                new Point(right.X1,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                        else if (left.H > right.H)
                        {
                            points.AddRange(
                                  new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(left.X2,left.H),
                                new Point(left.X2,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                        else//==
                        {
                            points.AddRange(
                                    new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H),                             
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                    }
                    if (left.X1 == right.X1 && left.X2 < right.X2)
                    {
                        if (left.H <= right.H)
                        {
                            points.AddRange(
                                   new List<Point>()
                            {
                                new Point(right.X1,0),
                                new Point(right.X1,right.H), 
                                new Point(right.X2,right.H),                           
                                new Point(right.X2,0),
                            });
                        }
                        else
                        {
                            points.AddRange(
                                  new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(left.X2,left.H),
                                new Point(left.X2,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                    }
                    if (left.X1 < right.X1 && left.X2 == right.X2)
                    {
                        if (left.H >= right.H)
                        {
                            points.AddRange(
                                   new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(left.X2,left.H),                           
                                new Point(left.X2,0),
                            });
                        }
                        else
                        {
                            points.AddRange(
                                  new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(right.X1,left.H),
                                new Point(right.X1,right.H),
                                new Point(right.X2,right.H),
                                new Point(right.X2,0),
                            });
                        }
                    }
                    if (left.X1 == right.X1 && left.X2 == right.X2)
                    {
                        points.AddRange(
                                   new List<Point>()
                            {
                                new Point(right.X1,0),
                                new Point(right.X1,Math.Max(left.H,right.H)), 
                                new Point(right.X2,Math.Max(left.H,right.H)),                           
                                new Point(right.X2,0),
                            });
                    }
                }
                #endregion
                #region Lx1<=Rx1<Rx2<=Lx2
                if (left.X1 <= right.X1 && right.X2 <= left.X2)
                {
                    //if (left.X1 == right.X1 && right.X2 == left.X2)
                    //{
                    //    points.AddRange(
                    //                   new List<Point>()
                    //        {
                    //            new Point(right.X1,0),
                    //            new Point(right.X1,Math.Max(left.H,right.H)), 
                    //            new Point(right.X2,Math.Max(left.H,right.H)),                           
                    //            new Point(right.X2,0),
                    //        });
                    //}
                    if (left.X1 < right.X1 && right.X2 < left.X2)
                    {
                        if (right.H <= left.H)
                        {
                            points.AddRange(
                                           new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(left.X2,left.H),                           
                                new Point(left.X2,0),
                            });
                        }
                        else
                        {
                            points.AddRange(
                                               new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(right.X1,left.H), 
                                new Point(right.X1,right.H),                           
                                new Point(right.X2,right.H),
                                new Point(right.X2,left.H),
                                new Point(left.X2,left.H), 
                                new Point(left.X2,0),
                            });
                        }
                    }
                    //if (left.X1 < right.X1 && right.X2 == left.X2)
                    //{
                    //    if (right.H <= left.H)
                    //    {
                    //        points.AddRange(
                    //                       new List<Point>()
                    //        {
                    //            new Point(left.X1,0),
                    //            new Point(left.X1,left.H), 
                    //            new Point(left.X2,left.H),                           
                    //            new Point(left.X2,0),
                    //        });
                    //    }
                    //    else
                    //    {
                    //        points.AddRange(
                    //                       new List<Point>()
                    //        {
                    //            new Point(left.X1,0),
                    //            new Point(left.X1,left.H), 
                    //            new Point(right.X1,left.H), 
                    //            new Point(right.X1,right.H),
                    //            new Point(right.X2,right.H),                           
                    //            new Point(right.X2,0),
                    //        });
                    //    }
                    //}
                    if (left.X1 == right.X1 && right.X2 < left.X2)
                    {
                        if (right.H <= left.H)
                        {
                            points.AddRange(
                                           new List<Point>()
                            {
                                new Point(left.X1,0),
                                new Point(left.X1,left.H), 
                                new Point(left.X2,left.H),                           
                                new Point(left.X2,0),
                            });
                        }
                        else
                        {
                            points.AddRange(
                                           new List<Point>()
                            {
                                new Point(right.X1,0),
                                new Point(right.X1,right.H), 
                                new Point(right.X2,right.H), 
                                new Point(right.X2,left.H),
                                new Point(left.X2,left.H),                           
                                new Point(left.X2,0),
                            });
                        }
                    }
                }
                #endregion
                return points;
            }

    合并两个轮廓线

       private static List<Point> mergeTwoOutLinesImpl(List<Point> L1, List<Point> L2)
            {
                int cursorL = 0;
                int cursorR = 0;
                int min = Convert.ToInt32(Math.Min(L1[0].X, L2[0].X));
                int max = Convert.ToInt32(Math.Max(L1.Last().X, L2.Last().X));
                List<Point> points = new List<Point>();
                points.Add(new Point(min, 0));
                for (double x = min; x <= max; x = x + 0.5)
                {
                    int y1 = -1, y2 = -1;
                    if (cursorL <= L1.Count - 2)
                    {
                        if (L1[cursorL].X == x && L1[cursorL].X == L1[cursorL + 1].X)
                        {
                            y1 = Math.Max(L1[cursorL].Y, L1[cursorL + 1].Y);
                            cursorL = cursorL + 2;
    
                        }
                        else if (x > L1[0].X && x < L1[cursorL].X)
                        {
                            y1 = L1[cursorL].Y;
                        }
                    }
                    if (cursorR <= L2.Count - 2)
                    {
                        if (L2[cursorR].X == x && L2[cursorR].X == L2[cursorR + 1].X)
                        {
                            y2 = Math.Max(L2[cursorR].Y, L2[cursorR + 1].Y);
                            cursorR = cursorR + 2;
    
                        }
                        else if (x > L2[0].X && x < L2[cursorR].X)
                        {
                            y2 = L2[cursorR].Y;
                        }
                    }
    
                    if (points.Count >= 3)
                    {
                        //当前水平线上已经存在两个等高的点,此时只需修改第二个点的x坐标以延伸水平线,此种情况不需要拐点
                        if (points[points.Count - 1].Y == points[points.Count - 2].Y && points[points.Count - 1].Y == Math.Max(y1, y2))
                        {
                            points[points.Count - 1].X = x;
                        }
                        else//此时需添加拐点,分为两种情况,一种是新添加的点与拐点x坐标相同,另一种是y坐标相同
                        {
                            //第一种情况,新的y值大于上一个点的y
                            if (Math.Max(y1, y2) > points[points.Count - 1].Y)
                            {
                                if (points[points.Count - 1].Y == points[points.Count - 2].Y)
                                {
                                    points[points.Count - 1].X = x;//水平线上已经存在两个点,改造第二个点x坐标到拐点位置
                                }
                                else
                                {
                                    points.Add(new Point(x, points[points.Count - 1].Y));//新拐点
                                }
                            }
                            //第二种情况,新的y值小于上一个点的y
                            if (Math.Max(y1, y2) < points[points.Count - 1].Y)
                            {
                                points.Add(new Point(points[points.Count - 1].X, Math.Max(y1, y2)));//新拐点
                            }
                            points.Add(new Point(x, Math.Max(y1, y2)));//添加水平线的第2个点
                        }
                    }
                    else
                    {
                        points.Add(new Point(x, Math.Max(y1, y2)));
                    }
                }
                points.Add(new Point(max, 0));
                return points;
            }

    //算法的调用方法,随机生成10万个建筑,其中x坐标范围为1-2000,高度不超过50。

    static void Main(string[] args)
            {
                 Building[] blds = OutLineUtility.initBuildings(100000, 1, 50, 2000);
                MergeBuildings(blds);
                Console.ReadKey();
            }
    public static void MergeBuildings(Building[] blds)
            {
                Stopwatch sw = new Stopwatch();
                Console.WriteLine("Start 1st Algorithm!");
                sw.Start();      
                List<Point> result = OutLineUtility.MergeBuildings(blds, 0, blds.Length - 1);
                sw.Stop();
                Console.WriteLine("Complete!Execution time:{0}s", sw.Elapsed.TotalSeconds);
    
                Console.Write("Calculated outline:");
                OutputOutlineToConsole(result);
                Console.WriteLine();
            }    
     private static void OutputOutlineToConsole(List<Point> result)
            {
                for (int i = 0; i < result.Count; i++)
                {
                    if (i % 2 == 0)
                    {
                        Console.Write(result[i].X);
                        Console.Write(" ");
                    }
                    if (i % 2 == 1)
                    {
                        Console.Write(result[i].Y);
                        Console.Write(" ");
                    }
                }
            }                    

    方法一完毕。

    另外考虑,是否可以把输入的建筑看成是轮廓线,这样在分治法里面直接合并轮廓线(试验得知,此方法比上面的方法要慢)

    调用算法二的方法

    Console.WriteLine("Start 2nd Algorithm!");
                sw.Restart();
                List<Point>[] Lps = new List<Point>[blds.Length];
                for (int i = 0; i < blds.Length; i++)
                {
                    Lps[i] = OutLineUtility.convertSingleBuilding2Outline(blds[i]);
                }
    
                result = OutLineUtility.MergeBuildings2(Lps, 0, blds.Length - 1);
                sw.Stop();
                Console.WriteLine("Complete!Execution time:{0}s", sw.Elapsed.TotalSeconds);
    
                Console.Write("Calculated outline:");
                OutputOutlineToConsole(result);
                Console.WriteLine();

    算法主体

    public static List<Point> MergeBuildings2(List<Point>[] bldsOutline, int leftIndex, int rightIndex)
            {
                if (rightIndex - leftIndex <= 1)//one or two buildings
                {
                    return mergeTwoOutLinesImpl(bldsOutline[leftIndex], bldsOutline[rightIndex]);
                }
                else
                {
                    int middle = (rightIndex + leftIndex) / 2;
                    List<Point> firstOutlines = MergeBuildings2(bldsOutline, leftIndex, middle);
                    List<Point> secondOutlines = MergeBuildings2(bldsOutline, middle + 1, rightIndex);
                    return mergeTwoOutLinesImpl(firstOutlines, secondOutlines);
                }
            }
    public static List<Point> convertSingleBuilding2Outline(Building bld)
            {
                return new List<Point>() 
                {
                new Point(bld.X1,0),
                  new Point(bld.X1,bld.H),
                    new Point(bld.X2,bld.H),
                      new Point(bld.X2,0),            
                };
    
            }

    方法二完毕。

    下载源码

    作者:Andy Zeng
    欢迎任何形式的转载,但请务必注明出处。

    http://www.cnblogs.com/andyzeng/p/3670432.html

  • 相关阅读:
    线段树专辑—— pku 1436 Horizontally Visible Segments
    线段树专辑——pku 3667 Hotel
    线段树专辑——hdu 1540 Tunnel Warfare
    线段树专辑—— hdu 1828 Picture
    线段树专辑—— hdu 1542 Atlantis
    线段树专辑 —— pku 2482 Stars in Your Window
    线段树专辑 —— pku 3225 Help with Intervals
    线段树专辑—— hdu 1255 覆盖的面积
    线段树专辑—— hdu 3016 Man Down
    Ajax跨域访问
  • 原文地址:https://www.cnblogs.com/andyzeng/p/3670432.html
Copyright © 2011-2022 走看看