【发布时间】:2015-11-22 00:49:21
【问题描述】:
我试图解决一个编程竞赛问题。我几乎是这个部门的菜鸟(我想我有很多东西要学)。我试图解决这个问题,其中包括读取二维数组(n x m)并找出其中的斑点。斑点由连续的发光像素形成(由# 表示)。未点亮的像素(由. 表示)。我试图通过使用递归方法Blob::form() 找到一个blob。示例输入可能如下所示
1
6 6
#...#.
.#.#.#
##..#.
......
.#.#.#
#...#.
我匆忙想出了解决方案。而且数量不多。但与往常一样,它在最坏的情况下失败n = m = 1000,所有字符都是#。一个 1000 x 1000 的版本:
1
3 3
###
###
###
我假设的问题是堆栈溢出。我发现程序在形成 blob 时崩溃了。
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
int pat[1000][1000],n,m;
char a[1000][1000];
struct point
{
int x,y;
};
bool inBounds(point p)
{
if(p.x < n && p.x >=0 && p.y < m && p.y >= 0) return true;
else return false;
}
bool isAblob(int i,int j)
{
point p[8];
p[0].x = i-1; p[0].y = j;
p[1].x = i+1; p[1].y = j;
p[2].x = i+1; p[2].y = j+1;
p[3].x = i-1; p[3].y = j-1;
p[4].x = i-1; p[4].y = j+1;
p[5].x = i+1; p[5].y = j-1;
p[6].x = i; p[6].y = j-1;
p[7].x = i; p[7].y = j+1;
for(int k=0;k<8;k++)
{
if(inBounds(p[k]))
{
if(a[p[k].x][p[k].y] == '#') return true;
}
}
return false;
}
class Blob
{
public:
long long int pow;
Blob(int i, int j)
{
this->pow = 0;
point po;
po.x=i;
po.y=j;
this->form(&po);
}
int getPow()
{
return this->pow;
}
void form ( point *p)
{
if(inBounds(*p))
{
if(a[p->x][p->y] == '#' && !pat[p->x][p->y])
{
a[p->x][p->y] = 1;
this->pow++;
point *e = new point;
e->x = p->x-1; e->y = p->y;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x+1; e->y = p->y;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x+1; e->y = p->y+1;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x-1; e->y = p->y-1;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x-1; e->y = p->y+1;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x+1; e->y = p->y-1;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x; e->y = p->y-1;if(pat[e->x][e->y] == 0)form(e);
e->x = p->x; e->y = p->y+1;
if(pat[e->x][e->y] == 0)form(e);
}
}
return;
}
};
int main()
{
int t;
cin >> t;
for (int q = 0; q < t; q++)
{
cin >> n >> m;
int bnum = 0;
Blob *b[(n*m)/2];
vector <int> pows;
cin.get();
for(int i=0;i<n;i++)
{
for(int j = 0; j<m;j++)
{
a[i][j] = cin.get();
pat[i][j] = 0;
}
cin.get();
}
for(int i=0;i<n;i++)
{
for(int j = 0; j<m;j++)
{
if(a[i][j] == '#' && pat[i][j] == 0)
{
if(isAblob(i,j))
{
bnum++;
b[bnum] = new Blob(i,j);
pows.push_back(b[bnum]->getPow());
}
else continue;
}
else continue;
}
}
sort(pows.begin(),pows.end());
cout << endl << bnum;
for(int i=1;i<=bnum;i++)
{
if(i==1) cout << endl;
if(i!=1) cout << " ";
cout << pows[i-1];
}
}
}
我确信我的代码有缺陷且效率低下。我想知道是否有人可以让我了解如何在未来避免这些问题。更好的实现也会有所帮助。但我正在寻找的是将来避免堆栈溢出的提示。
【问题讨论】:
-
我可以考虑将你的递归函数变成循环,确保它们是尾递归的(并依赖编译器来优化它们)或为分治算法选择更大的基本情况。例如,在您的方法
form中,我感觉您正在执行 BFS(广度优先搜索,将网格视为图形)(编辑:实际上这是一个 DFS,但您也可以将其变成循环并一个列表)。您可以将其转换为循环,而不是递归函数,该循环更新相邻点列表并在每次迭代时弹出第一个元素。 -
@Caninonos 我会试试的。你认为这可能避免堆栈溢出,因为我们限制了函数调用?
-
是的。关键是您的列表结构将(可能)将其对象存储在堆而不是堆栈中(编辑:通过“可能”,我的意思是:您当然可以创建一个列表结构在堆栈上保留内存,但这会很奇怪,动态数据结构通常使用堆)。 (此外,在这种特定情况下,我更喜欢 BFS 而不是 DFS,我认为它应该在某些极端情况下使列表增长得更少)
-
e已构造但从未删除。bnum从 1 开始使用 - 即,bnum== 0 从不使用。我不知道这些是否会对 1000 x 1000 产生任何影响,但清理起来不会有什么坏处。 -
此外,这里不需要那些
else continue;(并且无害)。没有它会更简单。
标签: c++ recursion stack-overflow