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

  • 相关阅读:
    git简单介绍
    ssh常用操作
    gentoo emerge简单用法
    golang程序因未知错误崩溃时如何记录异常
    RPC实现原理(HSF、dubbo) 从头开始(一)
    websocket
    tmpfs小结
    CURL常用命令
    SVN命令详解
    3.Linux Shell流程控制
  • 原文地址:https://www.cnblogs.com/andyzeng/p/3670432.html
Copyright © 2011-2022 走看看