轮廓问题/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很像, ...
随机推荐
- NHibernate3快速上手教程FluentNHibernate配置与DBHelper(已过期,有更好的)
很多学习NHibernate的新手很容易卡在配置文件这一关,正所谓万事开头难,上手后再配合官方文档就比较容易了. 网上关于配置文件的资料非常多,但由于版本的问题,许多老的教程中都没有明确指出类库的版本 ...
- Jedis 与 MySQL的连接线程安全问题
Jedis的连接是非线程安全的 下面是set命令的执行过程,简单分为两个过程,客户端向服务端发送数据,服务端向客户端返回数据,从下面的代码来看:从建立连接到执行命令是没有进行任何并发同步的控制 pub ...
- 使用open live writee写的博客
1. 安装包 下载链接:open live writer 2. 安装及使用教程 学习教程(转载他人) 3. 插入个图片 4. 写段代码 写不了,插件用不了 5. 插件使用: 参考文章:(http:// ...
- HADOOP docker(二):HDFS 高可用原理
1.环境简述2.QJM HA简述2.1为什么要做HDFS HA?2.2 HDFS HA的方式2.2 HSFS HA的结构2.3 机器要求3.部署HDFS HA3.1 详细配置3.2 部署HDF ...
- 互评Alpha版本——Thunder团队
基于NABCD评论作品 Hello World! :http://www.cnblogs.com/120626fj/p/7807544.html 欢迎来怼 :http://www.cnblogs.co ...
- c#数据库乱码
1.sql连接语句加charset=utf8: 2.不要使用odbcConnection. 在由utf8改为latin1时候,需要修改的地方: 1.连接数据库语句中的charset: 2.在sql语句 ...
- android入门 — AlertDialog对话框
常见的对话框主要分为消息提示对话框.确认对话框.列表对话框.单选对话框.多选对话框和自定义对话框. 对话框可以阻碍当前的UI线程,常用于退出确认等方面. 在这里主要的步骤可以总结为: 1.创建Aler ...
- <Android>对话框的使用
Android系统提供四种对话框:警告对话框(AlertDialog),进度对话框(ProgressDialog),日期选择对话框(DatePickerDialog)和时间选择对话框(TimePick ...
- SQL Server之看懂执行计划
在SQL Server中,选中一段SQL按Ctrl+L,就可以查看它的执行计划. 上面是一个执行计划的实例,在SQL Server中,执行计划是从右往左看的. SQL Server中,查找数据的方式有 ...
- 微信小程序项目笔记以及openId体验版获取问题
公司一直说要搞小程序,说了几个月,最近才算落地,一个很小的项目,就结果来讲,勉强让自己窥得小程序门径. 下面总结一下,为了弄好小程序,所学到的知识,以及项目中遇到的问题以及解决的办法.纯属个人见解. ...