介绍
本代码是用C#实现的。在开始代码解析之前,先把要用到的数据贴出来:
店铺id | 店铺宽度 | 店铺高度 | 入口位置 |
---|---|---|---|
1 | 9 | 6 | (5,6) |
2 | 5 | 6 | (1,6) |
3 | 5 | 6 | (1,6) |
4 | 4 | 5 | (1,5) |
5 | 4 | 5 | (1,5) |
6 | 4 | 5 | (1,5) |
7 | 4 | 5 | (1,5) |
8 | 4 | 4 | (1,4) |
9 | 4 | 4 | (1,4) |
10 | 4 | 4 | (1,4) |
11 | 4 | 4 | (1,4) |
12 | 4 | 4 | (1,4) |
在《开罗拉面店》里,是以左方向为x轴正方向,下方向为y轴正方向的,为了便于咱们计算,咱们可以将值顺时针旋转180°,变成咱们熟悉的坐标系,这样处理完再旋转回去就行。而这里的入口位置,则是相对于店铺左下角(也就是靠近原点的那一个方向)的格子的位置,举例说明:
这个店铺的入口相对于左下角那一格的位置,往左1格,往上4格,所以入口位置是(1,4)。
很容易可以计算出总共有
种排列情况,咱们的任务就是从这些排列情况中找到最优解!任务很艰巨是不是?
店铺类:Store
属性
Store类除了要有id、宽度、高度、入口位置之外,还要记录自己的位置,方便起见,以左下角那一格的坐标作为本店的位置;为了计算方便,还要储存本店扩大一行一列之后得到的矩形。
/// <summary>
/// 拉面店ID
/// </summary>
public int id;
/// <summary>
/// 拉面店左下角坐标
/// </summary>
public Point point;
/// <summary>
/// 拉面店高度
/// </summary>
public int height;
/// <summary>
/// 拉面店宽度
/// </summary>
public int width;
/// <summary>
/// 拉面店门的位置
/// </summary>
public Point door;
/// <summary>
/// 拉面店覆盖区域
/// </summary>
public Rectangle area;
方法
1. 判断店铺之间是否有重叠
这是与约束条件2对应的方法,在用BL算法排兵布阵的时候要判断扩大后的店铺有没有重叠,在铺完扩大后的店铺之后还要判断没扩大的店铺有没有重叠,所以需要写多个成员函数分别判断。
/// <summary>
/// 判断自己与另一个拉面店(扩大后的)是否会重叠
/// </summary>
/// <param name="box">另一个拉面店</param>
/// <returns>若重叠则返回false,不重叠则返回true</returns>
public bool overlap(Store box)
{
if ((point.X + area.Width-1 < box.point.X || point.X > box.point.X + box.area.Width-1) ||
(point.Y + area.Height-1 < box.point.Y || point.Y > box.point.Y + box.area.Height-1))
{
return true;
}
return false;
}
/// <summary>
/// 判断自己与另一个拉面店是否会重叠
/// </summary>
/// <param name="box">另一个拉面店</param>
/// <returns>若重叠则返回false,不重叠则返回true</returns>
public bool overlap_pro(Store box)
{
if ((point.X + width-1 < box.point.X || point.X > box.point.X + box.width-1) ||
(point.Y + height-1 < box.point.Y || point.Y > box.point.Y + box.height-1))
{
return true;
}
return false;
}
/// <summary>
/// 判断本店与其他所有店是否有重叠
/// </summary>
/// <param name="ans">其他所有店</param>
/// <returns>有一个重叠就返回false,全部不重叠返回true</returns>
public bool if_available(List<Store> ans)
{
bool flag = true;
for (int i = 0; i < ans.Count; i++)
{
if (!overlap(ans[i]))
{
flag = false;
break;
}
}
return flag;
}
2. 深复制
在C#里,自定义类作为参数传递的时候默认是传地址的,但是在后面有一些函数在处理的时候是希望传值的(之后会详细说明),所以需要写一个深复制函数。
/// <summary>
/// 深复制
/// </summary>
/// <returns>本店深复制后得到的新店</returns>
public Store Clone()
{
Store ans = new Store(id, new Point(point.X, point.Y), width, height, new Point(door.X, door.Y));
return ans;
}
3. 旋转
对应于约束条件7的成员函数。
/// <summary>
/// 旋转
/// </summary>
public void rotate()
{
int t = height;
height = width;
width = t;
door = new Point(door.Y, door.X);
area = new Rectangle(new Point(0, 0), new Size(width + 1, height + 1));
}
4. BL算法与改进
实现BL算法的第一步是将店铺放到右上角边界的位置,所以需要判断右上角边界那个位置能不能放得下,会不会和其他店铺重叠,如果能放得下的话,就可以开始玩“俄罗斯方块”了。对于BL算法,店铺需要判断能往下多少格,往左多少格;对于BL的改进算法,可以把往下移动和往左移动合并成一个动作。
/// <summary>
/// 判断本店能否在当前位置填充
/// </summary>
/// <param name="W">当前位置横坐标</param>
/// <param name="H">当前位置纵坐标</param>
/// <param name="ans">已填充的拉面店</param>
/// <returns>可以填充返回true,不能填充返回false</returns>
public bool set(int W, int H, List<Store> ans)
{
point.X = W - area.Width;
point.Y = H - area.Height;
return if_available(ans);
}
/// <summary>
/// 向下移动
/// </summary>
/// <param name="store">当前已填充的拉面店</param>
/// <returns>可以移动的最大步数</returns>
public int go_down(List<Store> store)
{
int move = 0;
for (int i = 0; i < store.Count; i++)
{
if (area.Width + point.X > store[i].point.X && point.X < store[i].area.Width + store[i].point.X &&
point.Y >= store[i].point.Y + store[i].area.Height)
{
int l = store[i].point.Y + store[i].area.Height;
if (move < l)
{
move = l;
}
}
}
move = point.Y - move;
point.Y -= move;
return move;
}
/// <summary>
/// 向左移动
/// </summary>
/// <param name="store">当前已填充的拉面店</param>
/// <returns>可以移动的最大步数</returns>
public int go_left(List<Store> store)
{
int move = 0;
for (int i = 0; i < store.Count; i++)
{
if (area.Height + point.Y > store[i].point.Y && point.Y < store[i].area.Height + store[i].point.Y &&
point.X >= store[i].point.X + store[i].area.Width)
{
int l = store[i].point.X + store[i].area.Width;
if (move < l)
{
move = l;
}
}
}
move = point.X - move;
point.X -= move;
return move;
}
/// <summary>
/// BL算法加强版
/// </summary>
/// <param name="store">当前已填充的拉面店</param>
/// <returns>可以移动的最大步数</returns>
public Point BL_plus_move(List<Store> store)
{
int down = 0;
int left = 0;
for (int i = 0; i < store.Count; i++)
{
if (point.X + area.Width > store[i].point.X && point.X < store[i].point.X + store[i].area.Width &&
point.Y >= store[i].point.Y + store[i].area.Height)
{
int l = store[i].point.Y + store[i].area.Height;
if (down < l)
{
down = l;
left = store[i].point.X;
}
}
}
down = point.Y - down;
point.Y -= down;
int move = 0;
for (int i = 0; i < store.Count; i++)
{
if (point.Y + area.Height > store[i].point.Y && point.Y < store[i].point.Y + store[i].area.Height &&
point.X >= store[i].point.X + store[i].area.Width)
{
int l = store[i].point.X + store[i].area.Width;
if (move < l)
{
move = l;
}
}
}
left = Math.Min(point.X + area.Width - left, point.X - move);
point.X -= left;
return new Point(down, left);
}
至此一个简单的Store类就基本实现了,接下来就是个体类的实现。