求一个搜索算法

www_adintr_com 2008-12-25 02:44:34
在一个 100 * 100 的格子区域内分布着不规则的可建筑的区域,在这个区域上可以修建 2 * 3, 3 * 2, 4 * 5 等不同长宽的矩形建筑,现在要找出一种浪费的空间最少,且能修建更多大面积建筑的方法。修建更多大面积建筑是指在浪费的空间相同的情况下优先修建 4 * 5 而不是 2 * 3.

输入: 一个 100 * 100 大小的 bool 数组, 为 true 的区域表示可以修建,为 false 的区域为不可修建。
输出:一个包含修建的建筑列表的数组, 建筑的信息包括建筑的位置,宽和高。

我自己写了个算法,效率太低了,当浪费的空间太大时非常慢,消耗的内存也太大,搜索不出来,看看各位高手有什么好的办法?

附我自己的代码:


struct SearchBuild
{
SearchBuild(unsigned char x, unsigned char y, unsigned char w, unsigned char h)
: m_x(x), m_y(y), m_w(w), m_h(h)
{};

unsigned char m_x, m_y, m_w, m_h;
};

struct SearchNode
{
SearchNode() : m_grids(GRIDMAXCOUNT * GRIDMAXCOUNT) {};

std::vector<bool> m_grids;
std::vector<SearchBuild> m_builds;
unsigned char m_x;
unsigned char m_y;
unsigned short m_wasteSpace;

bool operator<(const SearchNode& rhs) const
{
int myw = m_wasteSpace;
int hew = rhs.m_wasteSpace;

if(myw < hew)
{
return true;
}
else if(myw > hew)
{
return false;
}
else
{
return m_builds.size() > rhs.m_builds.size();
}
}

bool IsFull()
{
return m_y >= GRIDMAXCOUNT;
}

bool NextPoint()
{
++m_x;

while(true)
{
if(m_x >= GRIDMAXCOUNT)
{
m_x = 0;
++m_y;

continue;
}

if(m_y >= GRIDMAXCOUNT)
{
return false;
}

if(m_grids[m_y * GRIDMAXCOUNT + m_x])
{
return true;
}
else
{
++m_x;
}
}

return false;
}

bool HaveSpace(int width, int height)
{
for(int i = m_x; i < m_x + width; ++i)
{
for(int j = m_y; j < m_y + height; ++j)
{
if(!m_grids[j * GRIDMAXCOUNT + i])
return false;
}
}

return true;
}

void AddBuild(int width, int height)
{
for(int i = m_x; i < m_x + width; ++i)
{
for(int j = m_y; j < m_y + height; ++j)
{
m_grids[j * GRIDMAXCOUNT + i] = false;
}
}

m_builds.push_back(SearchBuild(m_x, m_y, width, height));
}
};


void CTestDlg::OnBnClickedButton1()
{
std::multiset<SearchNode> searchTable;

SearchNode node;
SearchNode newNode;

node.m_x = 0;
node.m_y = 0;
node.m_wasteSpace = 0;

// 下面这段是读入 100 * 100 的区域数据,不用管它
const GridVector& vec = mc_SceneShow.GetGridVec();
for(int x = 0; x < GRIDMAXCOUNT; ++x)
{
for(int y = 0; y < GRIDMAXCOUNT; ++y)
{
if(vec[x][y] == GT_TOBUILD)
node.m_grids[y * GRIDMAXCOUNT + x] = true;
else
node.m_grids[y * GRIDMAXCOUNT + x] = false;
}
}

searchTable.insert(node);

while(!searchTable.empty())
{
node = *searchTable.begin();
searchTable.erase(searchTable.begin());

if(node.IsFull())
{
break;
}

if(node.HaveSpace(5, 4))
{
newNode = node;
newNode.AddBuild(5, 4);
newNode.NextPoint();

searchTable.insert(newNode);
}

if(node.HaveSpace(2, 3))
{
newNode = node;
newNode.AddBuild(2, 3);
newNode.NextPoint();

searchTable.insert(newNode);
}

if(node.HaveSpace(3, 2))
{
newNode = node;
newNode.AddBuild(3, 2);
newNode.NextPoint();

searchTable.insert(newNode);
}

node.NextPoint();
node.m_wasteSpace++;
searchTable.insert(node);
}

// 下面是显示搜索结果,也不用管它
for(size_t i = 0; i < node.m_builds.size(); ++i)
{
mc_SceneShow.AddBuild(node.m_builds[i].m_x, node.m_builds[i].m_y, node.m_builds[i].m_w, node.m_builds[i].m_h);
}

mc_SceneShow.Invalidate(TRUE);
}

...全文
439 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
Follow_Inner_Heart 2012-09-11
  • 打赏
  • 举报
回复
mark
Kanderliu 2009-05-30
  • 打赏
  • 举报
回复
marking
绿色夹克衫 2009-01-05
  • 打赏
  • 举报
回复
可能是我说的不太清楚,n为边长,m为建筑种类的数量,以本题为例,是3种,所以基本上还是n^2级的,
以本题为例,计算量应该不超过10万次,只是不知是否会有漏检的情况

[Quote=引用 32 楼 flesharcher 的回复:]
引用 22 楼 litaoye 的回复:
写了一个算纯矩形的,没有考虑相同面积下4*5和2*3的问题(如果考虑的话,效率恐怕会下降到n^3)

不知道有了这些数据,对lz后面的程序能否有些帮助

效率大概为(m+k)*n^2
m为建筑的数量,k为建筑最宽的边+建筑最长的边,n为空地的大小



不清楚这里的N为空地的大小是指空地的面积还是边长,如果是面积感觉太吓人了。

而且有些奇怪的是m 本身是你算法的结果才对,为什么会成为算法效…
[/Quote]
flesharcher 2009-01-04
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 litaoye 的回复:]
写了一个算纯矩形的,没有考虑相同面积下4*5和2*3的问题(如果考虑的话,效率恐怕会下降到n^3)

不知道有了这些数据,对lz后面的程序能否有些帮助

效率大概为(m+k)*n^2
m为建筑的数量,k为建筑最宽的边+建筑最长的边,n为空地的大小

[/Quote]

不清楚这里的N为空地的大小是指空地的面积还是边长,如果是面积感觉太吓人了。

而且有些奇怪的是m 本身是你算法的结果才对,为什么会成为算法效率的一个参数?
flesharcher 2009-01-04
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 adlay 的回复:]
不错,看起来这个对于纯矩形来说应该是可行的。
不知道生成 100 * 100 内所有的完美覆盖花了多少时间? 如果的时间太长的话可以把它预存下来
[/Quote]

这种问题要想得到全局的最优是很困难的,好像一般实际中会选择获得一个“足够优”的解的算法。

常见的选择是做遗传算法。
www_adintr_com 2008-12-31
  • 打赏
  • 举报
回复
不错,看起来这个对于纯矩形来说应该是可行的。
不知道生成 100 * 100 内所有的完美覆盖花了多少时间? 如果的时间太长的话可以把它预存下来
绿色夹克衫 2008-12-31
  • 打赏
  • 举报
回复
差不多吧,不知道这个算法是否存在问题,大概试了几个,好像都还正确。

先生成可完美覆盖的矩形,不知道会不会有漏解,方法是先生成每行的完美覆盖,然后利用行的组合生成整个矩阵的,

比如:[2,6]可完美覆盖(2个2*3),[3,6]可完美覆盖(3个3*2),因此[5,6]可完美覆盖。

然后再将不能完美覆盖的矩形,分别从横向和纵向两个方向划分为2个矩形,然后计算每一种浪费的面积,取最小值。

[Quote=引用 27 楼 adlay 的回复:]
引用 22 楼 litaoye 的回复:
写了一个算纯矩形的,没有考虑相同面积下4*5和2*3的问题(如果考虑的话,效率恐怕会下降到n^3)

不知道有了这些数据,对lz后面的程序能否有些帮助

效率大概为(m+k)*n^2
m为建筑的数量,k为建筑最宽的边+建筑最长的边,n为空地的大小


C# code
private void button2_Click(object sender, EventArgs e)
{
int[][] buildingSize = new int[][] { new …
[/Quote]
www_adintr_com 2008-12-31
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 atlight 的回复:]
引用 16 楼 adlay 的回复:
引用 15 楼 dlyme 的回复:
引用 12 楼 adlay 的回复:
只要我们做出的每一步都是最优的就可以保证结果是最优的,


A* 算法.... 你怎么知道你这一步是全局最优的? 如果你每一步的确的全局优的.那么你得到最优解
问题在于.程序执行时,你最多只能得到当前最优.
A* 算法没你想的那么好.评估函数太难写.
[/Quote]

恩, A* 本身很简单,评估函数才是体现价值的地方,一个好的评估函数可以让算法达到效率与效果之间的完美平衡。
在实际应用中对于这个结果是 100% 的理论最优还是 99% 的最优可能区别不大,但搜索效率相差很大。
一个好的评估函数可以让整个搜索过程象人脑一样智能。我就一直在想,如果是人在进行这个过程的时候,影响他做出某一步选择的因素到底是什么?
www_adintr_com 2008-12-31
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 litaoye 的回复:]
写了一个算纯矩形的,没有考虑相同面积下4*5和2*3的问题(如果考虑的话,效率恐怕会下降到n^3)

不知道有了这些数据,对lz后面的程序能否有些帮助

效率大概为(m+k)*n^2
m为建筑的数量,k为建筑最宽的边+建筑最长的边,n为空地的大小


C# code
private void button2_Click(object sender, EventArgs e)
{
int[][] buildingSize = new int[][] { new int[] { 2, 3 }, new int[] { 3, …
[/Quote]

没看懂, 是通过尝试用不同的完美覆盖的矩形来放寻找浪费空间最少的那个完美覆盖矩形吗?
roadblossom 2008-12-29
  • 打赏
  • 举报
回复
up
atlight 2008-12-28
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 adlay 的回复:]
引用 15 楼 dlyme 的回复:
引用 12 楼 adlay 的回复:
只要我们做出的每一步都是最优的就可以保证结果是最优的,
[/Quote]

A* 算法.... 你怎么知道你这一步是全局最优的? 如果你每一步的确的全局优的.那么你得到最优解
问题在于.程序执行时,你最多只能得到当前最优.
A* 算法没你想的那么好.评估函数太难写.
Super.Jiju 2008-12-27
  • 打赏
  • 举报
回复
up````````````````````
fulton_xc 2008-12-27
  • 打赏
  • 举报
回复
mark
绿色夹克衫 2008-12-26
  • 打赏
  • 举报
回复
写错了,不好意思,应该是[0,4]是指第1行第5列,和[1,5]第2行第6列,不可能同时被覆盖
同样,[1,5]和[2,6]也不可能同时被覆盖。

[Quote=引用 19 楼 adlay 的回复:]
引用 13 楼 litaoye 的回复:
我先说个预处理的思路,如果建筑只包括4*5,2*3,3*2这3个规格,有些情况是根本不可能被覆盖到的,因此可以先将这些小块设为true,
也许会降低一些计算量。

比如(0为空位置):

0000011100
0000001100
0000000111
0000000000
0000000000

右上的4个0是不可能被覆盖的。

[0,5][1,6]这两个点不可能同时被覆盖
[1,6][2,7]这两个点不可能同时被覆盖

这样的情况下,也许可以…
[/Quote]
www_adintr_com 2008-12-26
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 litaoye 的回复:]
不知道是否可以先计算出一些可以完美覆盖的矩形的尺寸,比如:

2*3 2*6 2*9 ...... 2*99
3*2 3*4 3*6 ...... 3*100
4*3 4*5 4*6 4*8 4*9 4*10 ...... 4* 100
5*6 5*12 5*18......5*96
..........

然后判断可否将空地划分为若干个这个集合里面的矩形。
[/Quote]

恩,不错,如果我们搜索某个点后剩下的区域是个完美覆盖矩形的尺寸,那这个选择就是一个完美的选择。
至于搜索的时候直接使用完美尺寸来进行搜索我还想不到什么操作方法。

我现在正在考虑一个先按照长宽算一个最大的完美覆盖工厂的尺寸,然后把这个尺寸放到各个可能的位置,比较剩下的边角是否会产生死点(不能在修建筑的可建筑区域),然后来调整这块区域的大小和位置。最后在每一个边角的小面积区域可以全搜索来找到最佳的覆盖边角的方法。
www_adintr_com 2008-12-26
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 litaoye 的回复:]
我先说个预处理的思路,如果建筑只包括4*5,2*3,3*2这3个规格,有些情况是根本不可能被覆盖到的,因此可以先将这些小块设为true,
也许会降低一些计算量。

比如(0为空位置):

0000011100
0000001100
0000000111
0000000000
0000000000

右上的4个0是不可能被覆盖的。

[0,5][1,6]这两个点不可能同时被覆盖
[1,6][2,7]这两个点不可能同时被覆盖

这样的情况下,也许可以舍掉[1,6]这个点
[/Quote]

进行预处理是非常好的思路,你说的去掉右上角 4 个 0 对算法来说是非常有效的,但是实际使用的时候可能会很少输入这种数据,输入方面可以保证的是输入的数据里面的每个有效点上都至少可以建一个 2 * 3 或 3 * 2。
后面那个没看懂, [0,5] 是值的 第 1 列,第 6 行?不存在! 第 1 行第 6 列? 这个位置是障碍!
ooily 2008-12-26
  • 打赏
  • 举报
回复
up
www_adintr_com 2008-12-26
  • 打赏
  • 举报
回复
另外关于题目我再补充一下:
这是从一个有实际用途的模型中抽出来的,不是一个臆想出来的东西。
浪费的空间最少,修建尽可能大的建筑,这是我们的目标,但是不一定完全能够实现,在这种情况下希望达到的效果是在一定的时间和空间复杂度内做到尽可能少浪费的土地,修建更大的建筑。
具体来说, 1G 内存空间 10 分钟 的复杂度内能找到浪费最小土地的修建方法就是最好的算法。
www_adintr_com 2008-12-26
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 dlyme 的回复:]
引用 12 楼 adlay 的回复:
找到最优解当然不等于全搜索,如果我明确的知道这个地方放一个 4 * 5 的不会浪费空间,而放二个 2 * 3 和一个 3 * 2 的话一定会浪费 2 格空间,我何必还要对后面的那种情况搜索下去呢?

A* 算法也完全说明了找最优解根本不需要进行全搜索。
只要我们做出的每一步都是最优的就可以保证结果是最优的,只要找到 1 个结果就可以了,要是把所有情况都搜索完,我们可以估计一下, 三种建筑的平均面积大…
[/Quote]

不管是否把 A* 算法当做全搜索算法,我想达到的就是 A* 这种效果,通过某种搜索策略,能够在搜索和比较很少内容的情况下就找到一个最优的结果。
而我想问的问题就是你所问我的:
怎样明确知道某个地方放一个 4 * 5 的不会浪费空间,而放二个 2 * 3 和一个 3 * 2 的话一定会浪费 2 格空间?
怎样“找到一种目标函数,在前期就能立刻断言哪种方案是最佳的?”
要兼顾各种各样奇怪的建筑物尺寸、还有各处设置了障碍点。我们如何断定怎么放才是最优的?

我所希望的是在每一步的时候都能通过横向比较,对剩余空间的一些预判来做出一个很优的选择,或者说对每一种选择都进行一个估价,之后的搜索不会进行太多的回溯。而这个比较,预判,估价的搜索策略是我提这个问题主要关注的内容。

至于你提供的 DLX 算法只是在操作一个回溯的过程中提供一些数据结构和算法来提高了回溯的速度(不知我这样理解对不对?),这个一开始就不在我的关注点上,它或许能把我现在的速度提高 2 倍,提高 5 倍,或者说提高 10 倍,但仍然解决不了问题,我要的是提高上万倍,上亿倍和更大,而这个只能通过优化搜索策略减小搜索量来完成。

最后,不管怎么说仍然很感谢你的参与,或许我找到好的搜索策略之后会再来考虑使用 DLX 算法来进一步提高点速度。
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 adlay 的回复:]
找到最优解当然不等于全搜索,如果我明确的知道这个地方放一个 4 * 5 的不会浪费空间,而放二个 2 * 3 和一个 3 * 2 的话一定会浪费 2 格空间,我何必还要对后面的那种情况搜索下去呢?

A* 算法也完全说明了找最优解根本不需要进行全搜索。
只要我们做出的每一步都是最优的就可以保证结果是最优的,只要找到 1 个结果就可以了,要是把所有情况都搜索完,我们可以估计一下, 三种建筑的平均面积大约为 10 格左右,那就是说每…
[/Quote]
问题就在这里:“如果我明确的知道这个地方放一个 4 * 5 的不会浪费空间...”,那你是怎样明确知道的呢?
只有4*5大小当然可以这么说,问题是你是在一个大范围内,同时还要兼顾各种各样奇怪的建筑物尺寸、还有各处设置了障碍点。正是因为我们断定不了怎么放才是最优的,所以才需要搜索和判断。

话题扯开一点,说到A*算法,它当然是一种全搜索的算法。只不过是在搜索过程中制定了优先策略,在这种策略的指引下我们很可能会更快地找到最优解,而不用盲目地挣扎。
再回到你这个问题,它与A*找最短路径又不一样,麻烦在于不好找到一种目标函数,在前期就能立刻断言哪种方案是最佳的,所以需要搜索。
“只要我们做出的每一步都是最优的就可以保证结果是最优的”,尴尬的是,我们连当前的优劣都不知道,尤其是在前期搜索刚开始的时候。

DLX更象是一种数据结构,前面已经说过,它的本质还是回溯。
我们可以在搜索过程中仿效A*来制定一些优先策略,也可以通过一些指标(比如说已经确定会被“浪费”的空间大小)来剪枝和优化,与你前面说的并不存在矛盾。

我在这里只是提供一种可借鉴的思路,如果你觉得毫无价值,那么ok,就到这里吧。
加载更多回复(15)

33,009

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧