——进步基于积累。
最小凸包问题:
平面上有n个点p1,p2, ..., pn, 要求求出一个面积最小的凸多边形,使得这个多边形包含所有平面上的点。
分析:
ptList[n] n>2
for i=0,to n
{
for j=i+1, to n
{
//判断其他所有的点是否都在该直线的一边
//if 所有的点都在线的一边,则说明是凸边,可记录,否则退出循环
//判断点和线的关系,可以通过线解析方程,也可以通过向量叉积计算
f(x,y) (直线方程)=ax+by+c
bool isEnvelope = true;
int flag = 0; //在直线上
for k=0, t0 n
{
int tempFlag = f(ptList[i],ptList[j])
if tempFlag == 0
continue;//在直线上不予处理
if flag == 0
{
flag = tempFlag;
continue;
}
if flag * tempFlag < 0
{
isEnvelope = false;
break;
}
}
if isEnvelope == true //是凸边
{
//进行处理
}
}
}
/// <summary>
/// 计算凸包
/// </summary>
class EnvelopeCompute
{
private int imgWidth;
private int imgHeight;
private int createCount;
private List<Point> ptLst;
private readonly int radius;
public int CreateCount
{
set
{
this.createCount = value;
}
}
public EnvelopeCompute(int width, int height, int count)
{
this.imgWidth = width;
this.imgHeight = height;
this.createCount = count;
this.ptLst = new List<Point>();
this.radius = 2;
}
/// <summary>
/// 获取随机生成点的图片
/// </summary>
/// <returns>图片</returns>
public Image GetRandomImag()
{
//随机产生点
RandomCratePtLst();
//绘制图片
Bitmap map = new Bitmap(this.imgWidth, this.imgHeight);
Graphics gfx = Graphics.FromImage(map);
gfx.Clear(Color.Gray);
foreach (Point item in this.ptLst)
{
//map.SetPixel(item.X, item.Y, Color.Red);
gfx.FillEllipse(Brushes.Red, item.X - radius, item.Y - radius, radius * 2, radius * 2);
}
gfx.Dispose();
return map;
}
/// <summary>
/// 获取凸包结果图片
/// </summary>
/// <returns>结果图片</returns>
public Image GetEnvelopeResult()
{
if (this.ptLst.Count < 2)
{
return null;
}
int leftIndex = -1;
int rightIndex = -1;
List<LineSegment> lines = ComputerEnvelope(ref leftIndex, ref rightIndex);
Bitmap map = new Bitmap(this.imgWidth, this.imgHeight);
Graphics gfx = Graphics.FromImage(map);
gfx.Clear(Color.White);
Pen pen = new Pen(Color.Green, 3);
//绘制凸包
foreach (LineSegment item in lines)
{
gfx.DrawLine(pen, item.StartPt, item.EndPt);
}
foreach (Point item in this.ptLst)
{
//map.SetPixel(item.X, item.Y, Color.Red);
gfx.FillEllipse(Brushes.Red, item.X - radius, item.Y - radius, radius * 2, radius * 2);
}
gfx.Dispose();
return map;
}
//随机生成点
private void RandomCratePtLst()
{
this.ptLst.Clear();
Random rd = new Random();
for (int i = 0; i < this.createCount; i++)
{
this.ptLst.Add(new Point(rd.Next(100, this.imgWidth-100), rd.Next(100, this.imgHeight-100)));
}
}
//蛮力计算凸包
private List<LineSegment> ComputerEnvelope(ref int leftIndex, ref int rightIndex)
{
List<LineSegment> lines = new List<LineSegment>();
//遍历每一个点和另一个点组成的线段
for (int i = 0; i < ptLst.Count; i++)
{
//确保两点之间不重复
for (int j = i + 1; j < ptLst.Count; j++)
{
//构建该线段方程ax+by+c=0
int a = ptLst[j].Y - ptLst[i].Y;
int b = ptLst[i].X - ptLst[j].X;
int c = ptLst[j].X * ptLst[i].Y - ptLst[i].X * ptLst[j].Y;
//遍历其他点是否在该线段的同一方向,如果是在直线上不予处理
int flag = 0; //标记0:在线上。
bool isEvelope = true; //标记是否是凸包边界
for (int k = 0; k < ptLst.Count; k++)
{
if (k == i || k == j)
{
continue;
}
//int temp=f(x,y)=ax+by+c
//if temp==0 (x,y)在线上,if temp>0 (x,y)在线一侧 if temp<0 (x,y)在线的另一侧
int temp = a * ptLst[k].X + b * ptLst[k].Y + c;
if (temp == 0)
{
continue;
}
if (flag == 0)
{
flag = temp;
continue;
}
if (flag * temp < 0) //在线的两端
{
isEvelope = false;
break;
}
}
//如果确实是在同一方向,则加入判断点内
if (isEvelope)
{
LineSegment segment = new LineSegment();
segment.StartPt = ptLst[i];
segment.EndPt = ptLst[j];
lines.Add(segment);
}
}
}
return lines;
}
/// <summary>
/// 线段
/// </summary>
class LineSegment
{
public Point StartPt;
public Point EndPt;
}
}