轮廓问题/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 = ; 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的更多相关文章
- W3School-CSS 轮廓(Outline)实例
CSS 轮廓(Outline)实例 CSS 实例 CSS 背景实例 CSS 文本实例 CSS 字体(font)实例 CSS 边框(border)实例 CSS 外边距 (margin) 实例 CSS 内 ...
- CSS:CSS 轮廓(outline)
ylbtech-CSS:CSS 轮廓(outline) 1.返回顶部 1. CSS 轮廓(outline) 轮廓(outline)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用. ...
- 轮廓(Outline) 实例
1.在元素周围画线本例演示使用outline属性在元素周围画一条线. <style type="text/css"> p{border:red solid thin;o ...
- 轮廓问题/Outline Problem-->改进的算法及时间复杂度分析
前面写过一篇关于轮廓算法的文章,是把合并建筑和合并轮廓是分开对待的,并且为了使轮廓合并的时候算法简单,对x坐标使用了double类型,然后对整形的x坐标数据进行合并.这样做是为了使得需找拐点的算法容易 ...
- CSS Outline(轮廓)
CSS Outline(轮廓) 一.CSS 轮廓(outline) 轮廓(outline)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用. CSS outline 属性规定元素轮廓 ...
- CSS基础(背景、文本、列表、表格、轮廓)
CSS 背景属性 属性 描述 background 简写属性,作用是将背景属性设置在一个声明中. background-attachment 背景图像是否固定或者随着页面的其余部分滚动. backgr ...
- CSS控制边界、边框与外轮廓
一.CSS控制边界 1.内边距 padding(内边距也叫内填充) padding-bottom 长度/百分比 元件下端边线的空隙 padding-left 长度/百分比 元件左端边线的空隙 padd ...
- outline详解
outline这个属性平时用的不太多,最近被问及专门研究一下这个属性的作用. CSS2加进来的outline属性,中文翻译过来是外轮廓. 神马是轮廓? 轮廓,指边缘:物体的外周或图形的外框. 那这样的 ...
- css3-13 如何改变文本框的轮廓颜色
css3-13 如何改变文本框的轮廓颜色 一.总结 一句话总结:outline使用和border很像,几乎一模一样,多了一个offset属性 1.轮廓outline如何使用? 使用和border很像, ...
随机推荐
- ubuntu samba配置注意事项
1. 下载samba前, ubuntu镜像源需要更新为国内源,否则samba的安装会非常慢 亲测,清华的镜像源速度满足要求. A.登录 https://mirrors.tuna.tsinghua.ed ...
- LeetCode 94 ——二叉树的中序遍历
1. 题目 2. 解答 2.1. 递归法 定义一个存放树中数据的向量 data,从根节点开始,如果节点不为空,那么 递归得到其左子树的数据向量 temp,将 temp 合并到 data 中去 将当前节 ...
- vue学习笔记之:为何data是一个方法
vue学习笔记之:为何data是一个方法 在vue开发中,我们可以发现,data中的属性值是在function中return出来的.可为何data必须是一个函数呢?我们先看官方的解释: 当一个组件被定 ...
- H5页面 绝对定位元素被 软键盘弹出时顶起
H5页面 绝对定位元素被 软键盘弹出时顶起 在h5页面开发的过程中,我们可能会遇到下面这个问题,当页面中有输入框的时候,系统自带的软盘会把按钮挤出原来的位置.那么我们该怎么解决呢?下面列出一下的方法: ...
- 使用 letter-space 后文字不能居中解决
letter-space:2em; text-align: center; 使用letter-space后和上面的字体对比明显没有居中: 选定元素后发现,每个字后面都被加了2em,不是不能居中而是因为 ...
- 20145214 《Java程序设计》第7周学习总结
20145214 <Java程序设计>第7周学习总结 教材学习内容总结 时间的度量 格林威治标准时间(GMT),现已不作为标准时间使用,即使标注为GMT(格林威治时间),实际上谈到的的是U ...
- Mininet实验 多个数据中心的拓扑网络实现
实验目的 掌握多数据中心网络拓扑的构建 掌握多数据中心数据交换过程 实验原理 主机间发送消息上报给交换机,交换机对收到的报文信息进行分析判断,如果交换机中存在此消息相对应的流表,则交换机直接下发流表, ...
- TCP系列18—重传—8、FACK及SACK reneging下的重传
一.介绍 FACK的全称是forward acknowledgement,FACK通过记录SACK块中系列号最大(forward-most)的SACK块来推测丢包信息,在linux中使用fackets ...
- VS2010中的sln,suo分别是什么含义
我们通过双击.sln加载出我们的工程. Visual Studio.NET采用两种文件类型(.sln和.suo)来存储特定于解决方案的设置,它们总称为解决方案文件.为解决方案资源管理器提供显示管理文件 ...
- 三次握手 四次握手 与socket函数的关系
“一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...