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

//已发布改进后的轮廓问题算法: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 = ; i < buildCount; i++)
{
int l = rndRange.Next(leftLimitInclusive, rightLimitInclusive);
int r = rndRange.Next(l + , rightLimitInclusive + );
int h = rndHeight.Next(, maxHeightLimitInclusive + );
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 <= )//one or two buildings
{
return mergeTwoBuildingsImpl(blds[leftIndex], blds[rightIndex]);
}
else
{
int middle = (rightIndex + leftIndex) / ;
List<Point> firstOutlines = MergeBuildings(blds, leftIndex, middle);
List<Point> secondOutlines = MergeBuildings(blds, middle + , 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,),
new Point(left.X1,left.H),
new Point(left.X2,left.H),
new Point(left.X2,),
new Point(right.X1,),
new Point(right.X1,right.H),
new Point(right.X2,right.H),
new Point(right.X2,),
});
}
else//==
{
if (left.H == right.H)
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
new Point(left.X1,left.H),
new Point(right.X2,right.H),
new Point(right.X2,),
});
}
else
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
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,),
});
}
}
}
#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,),
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,),
});
}
else if (left.H > right.H)
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
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,),
});
}
else//==
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
new Point(left.X1,left.H),
new Point(right.X2,right.H),
new Point(right.X2,),
});
}
}
if (left.X1 == right.X1 && left.X2 < right.X2)
{
if (left.H <= right.H)
{
points.AddRange(
new List<Point>()
{
new Point(right.X1,),
new Point(right.X1,right.H),
new Point(right.X2,right.H),
new Point(right.X2,),
});
}
else
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
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,),
});
}
}
if (left.X1 < right.X1 && left.X2 == right.X2)
{
if (left.H >= right.H)
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
new Point(left.X1,left.H),
new Point(left.X2,left.H),
new Point(left.X2,),
});
}
else
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
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,),
});
}
}
if (left.X1 == right.X1 && left.X2 == right.X2)
{
points.AddRange(
new List<Point>()
{
new Point(right.X1,),
new Point(right.X1,Math.Max(left.H,right.H)),
new Point(right.X2,Math.Max(left.H,right.H)),
new Point(right.X2,),
});
}
}
#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,),
new Point(left.X1,left.H),
new Point(left.X2,left.H),
new Point(left.X2,),
});
}
else
{
points.AddRange(
new List<Point>()
{
new Point(left.X1,),
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,),
});
}
}
//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,),
new Point(left.X1,left.H),
new Point(left.X2,left.H),
new Point(left.X2,),
});
}
else
{
points.AddRange(
new List<Point>()
{
new Point(right.X1,),
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,),
});
}
}
}
#endregion
return points;
}

合并两个轮廓线

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

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

static void Main(string[] args)
{
Building[] blds = OutLineUtility.initBuildings(, , , );
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, , blds.Length - );
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 = ; i < result.Count; i++)
{
if (i % == )
{
Console.Write(result[i].X);
Console.Write(" ");
}
if (i % == )
{
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 = ; i < blds.Length; i++)
{
Lps[i] = OutLineUtility.convertSingleBuilding2Outline(blds[i]);
} result = OutLineUtility.MergeBuildings2(Lps, , blds.Length - );
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 <= )//one or two buildings
{
return mergeTwoOutLinesImpl(bldsOutline[leftIndex], bldsOutline[rightIndex]);
}
else
{
int middle = (rightIndex + leftIndex) / ;
List<Point> firstOutlines = MergeBuildings2(bldsOutline, leftIndex, middle);
List<Point> secondOutlines = MergeBuildings2(bldsOutline, middle + , rightIndex);
return mergeTwoOutLinesImpl(firstOutlines, secondOutlines);
}
}
public static List<Point> convertSingleBuilding2Outline(Building bld)
{
return new List<Point>()
{
new Point(bld.X1,),
new Point(bld.X1,bld.H),
new Point(bld.X2,bld.H),
new Point(bld.X2,),
}; }

方法二完毕。

下载源码

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

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

轮廓问题/Outline Problem的更多相关文章

  1. W3School-CSS 轮廓(Outline)实例

    CSS 轮廓(Outline)实例 CSS 实例 CSS 背景实例 CSS 文本实例 CSS 字体(font)实例 CSS 边框(border)实例 CSS 外边距 (margin) 实例 CSS 内 ...

  2. CSS:CSS 轮廓(outline)

    ylbtech-CSS:CSS 轮廓(outline) 1.返回顶部 1. CSS 轮廓(outline) 轮廓(outline)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用. ...

  3. 轮廓(Outline) 实例

    1.在元素周围画线本例演示使用outline属性在元素周围画一条线. <style type="text/css"> p{border:red solid thin;o ...

  4. 轮廓问题/Outline Problem-->改进的算法及时间复杂度分析

    前面写过一篇关于轮廓算法的文章,是把合并建筑和合并轮廓是分开对待的,并且为了使轮廓合并的时候算法简单,对x坐标使用了double类型,然后对整形的x坐标数据进行合并.这样做是为了使得需找拐点的算法容易 ...

  5. CSS Outline(轮廓)

    CSS Outline(轮廓) 一.CSS 轮廓(outline) 轮廓(outline)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用. CSS outline 属性规定元素轮廓 ...

  6. CSS基础(背景、文本、列表、表格、轮廓)

    CSS 背景属性 属性 描述 background 简写属性,作用是将背景属性设置在一个声明中. background-attachment 背景图像是否固定或者随着页面的其余部分滚动. backgr ...

  7. CSS控制边界、边框与外轮廓

    一.CSS控制边界 1.内边距 padding(内边距也叫内填充) padding-bottom 长度/百分比 元件下端边线的空隙 padding-left 长度/百分比 元件左端边线的空隙 padd ...

  8. outline详解

    outline这个属性平时用的不太多,最近被问及专门研究一下这个属性的作用. CSS2加进来的outline属性,中文翻译过来是外轮廓. 神马是轮廓? 轮廓,指边缘:物体的外周或图形的外框. 那这样的 ...

  9. css3-13 如何改变文本框的轮廓颜色

    css3-13 如何改变文本框的轮廓颜色 一.总结 一句话总结:outline使用和border很像,几乎一模一样,多了一个offset属性 1.轮廓outline如何使用? 使用和border很像, ...

随机推荐

  1. ionic ios样式偏移解决方案。

    在css属性内增加: .item-ios [item-end] { //解决ios系统上尾部图标出现重影而增加的格式. margin: 0px -15.3px 0px 0px; margin-bott ...

  2. HPUX 11.31 MC/SG恢复丢失的锁盘

    有时候由于一些特殊的原因,用户的cluster中的锁盘信息丢失,或者需要更换锁盘,只要执行一个命令就可以了. #cmdisklock reset /dev/vglock:/dev/disk/diskX ...

  3. 【RL系列】Multi-Armed Bandit笔记——UCB策略与Gradient策略

    本篇主要是为了记录UCB策略与Gradient策略在解决Multi-Armed Bandit问题时的实现方法,涉及理论部分较少,所以请先阅读Reinforcement Learning: An Int ...

  4. 2.hbase原理(未完待续)

    hbase简介相关概念hmsterhregionserver表regionhstorememstorestorefilehfileblockcacheWALminorcompactmajorcompa ...

  5. vue学习笔记之:为何data是一个方法

    vue学习笔记之:为何data是一个方法 在vue开发中,我们可以发现,data中的属性值是在function中return出来的.可为何data必须是一个函数呢?我们先看官方的解释: 当一个组件被定 ...

  6. Beta完结--感想及吐槽

    Beta冲刺结束啦!!! Beta冲刺结束啦!!! Beta冲刺结束啦!!! 这时候每个人的心情肯定都是非常激动的.随着Beta冲刺的结束,折磨了我们一整个学期的软工实践也差不多结束了.(实在是太不容 ...

  7. PagedDataSource数据绑定控件和AspNetPager分页控件结合使用列表分页

    1.引用AspNetPager.dll. 2.放置Repeater数据绑定控件. <asp:Repeater ID="Repeater1" runat="serve ...

  8. BAT批处理(六)

    字符串处理 批处理有着具有非常强大的字符串处理能力,其功能绝不低于C语言里面的字符串函数集.批处理中可实现的字符串处理功能有:截取字符串内容.替换字符串特定字段.合并字符串.扩充字符串等功能.下面对这 ...

  9. TreeView的使用

    用于显示多级层次关系 每一项是一个节点,也就是一个Node,是一个TreeNode节点,Nodes是该控件节点的集合. selectedNode用户选中的节点,如果没有选中则为null 1. 当选中后 ...

  10. 域对象 pageContext request session servletContext

    pageContext 当前页面之内有效 request   当前的请求内有效 session 当前的会话内有效 servletContext 当前这次服务器生命周期内有效