我正在做练习和提高技能的任务,但我很难解决这个问题:
你有一张尺寸为w * h的地图。在这张地图的每个方框都是墙(洪水 保护)或什么也没有。水可以在八个方向流动并流动 在地图边缘的每个方框上。当然,水不能泛滥 箱子里面有防洪保护。你会得到地图的大小, 墙的数量和每个墙的位置。
一开始地图为空。你的任务是放置每个 一块墙告诉我们大面积的防水区域。
所以,我有适用的代码。但太慢了。限制是:地图的大小w和h(1≤w,h≤2000)
墙数:n(1≤n≤w×h)
我尝试了8路FloodFill算法,然后将其改进为8路ScanlineFill。它太慢了,在一半的测试条目中耗尽了时间。如果您需要,我会发布我的代码。
所以我的问题是:如何才能更快地提高算法?或者我对算法的选择完全错误,有更好的方法吗?谢谢你的建议。
测试条目:
Input:
4 4 //size w and h
10 // number of walls
1 1 //position of wall - first x, second y coordinate;
1 2
1 3
2 1
2 3
3 1
3 2
3 3
2 2
3 4
Output:
1 //how big are is covered against flood
2
3
4
5
6
7
9
9
10
示例输入说明:洪水墙的前8部分 保护区域大小3 x 3.第九部分完全没有效果, 因为盒子(2,2)已经受到保护。第十部分 不结束任何领土,因此只会贡献它 表面(仅添加1)。
我的代码:
#include<iostream>
using namespace std;
int **ostrov;
int area;
#define stackSize 16777216
int stack[stackSize];
int stackPointer;
int h, w;
bool pop(int
&x, int &y)
{
if (stackPointer > 0)
{
int p = stack[stackPointer];
x = p / h;
y = p % h;
stackPointer--;
return 1;
}
else
{
return 0;
}
}
bool push(int x, int y)
{
if (stackPointer < stackSize - 1)
{
stackPointer++;
stack[stackPointer] = h * x + y;
return 1;
}
else
{
return 0;
}
}
void emptyStack()
{
int x, y;
while (pop(x, y));
}
//The scanline floodfill algorithm using our own stack routines, faster
void floodFillScanlineStack(int x, int y, int newColor, int oldColor)
{
if (oldColor == newColor) return;
emptyStack();
int y1;
bool spanLeft, spanRight, spanDDLeft, spanDDRight, spanDULeft, spanDURight;
if (!push(x, y)) return;
while (pop(x, y))
{
y1 = y;
while (y1 >= 0 &&ostrov[x][y1] == oldColor) y1--;
y1++;
spanLeft = spanRight = spanDDLeft = spanDDRight = spanDULeft = spanDURight = 0;
while (y1 < h && ostrov[x][y1] == oldColor)
{
if (ostrov[x][y1] == oldColor)
pocet++;
ostrov[x][y1] = newColor;
if (!spanLeft && x > 0 && ostrov[x - 1][y1] == oldColor)
{
if (!push(x - 1, y1)) return;
spanLeft = 1;
}
else if (spanLeft && x > 0 && ostrov[x - 1][y1] != oldColor)
{
spanLeft = 0;
}
if (!spanRight && x < w - 1 && ostrov[x + 1][y1] == oldColor)
{
if (!push(x + 1, y1)) return;
spanRight = 1;
}
else if (spanRight && x < w - 1 && ostrov[x + 1][y1] != oldColor)
{
spanRight = 0;
}
if (!spanDDLeft && x > 0 && y1 + 1 < h && ostrov[x - 1][y1 + 1] == oldColor)
{
if (!push(x - 1, y1 + 1)) return;
spanDDLeft = 1;
}
else if (spanDDLeft && x > 0 && y1 + 1 < h && ostrov[x - 1][y1 + 1] != oldColor)
{
spanDDLeft = 0;
}
if (!spanDDRight && x + 1 < w && y1 + 1 < h && ostrov[x + 1][y1 + 1] == oldColor)
{
if (!push(x + 1, y1 + 1)) return;
spanDDRight = 1;
}
else if (spanDDRight && x + 1 < w && y1 + 1 < h && ostrov[x + 1][y1 + 1] != oldColor)
{
spanDDRight = 0;
}
if (!spanDULeft && x > 0 && y1 > 0 && ostrov[x - 1][y1 - 1] == oldColor)
{
if (!push(x - 1, y1 - 1)) return;
spanDULeft = 1;
}
else if (spanDULeft && x > 0 && y1 > 0 && ostrov[x - 1][y1 - 1] != oldColor)
{
spanDULeft = 0;
}
if (!spanDURight && x + 1 < w && y1 > 0 && ostrov[x + 1][y1 - 1] == oldColor)
{
if (!push(x + 1, y1 - 1)) return;
spanDURight = 1;
}
else if (spanDURight && x + 1 < w && y1 > 0 && ostrov[x + 1][y1 - 1] != oldColor)
{
spanDURight = 0;
}
y1++;
}
}
}
int main()
{
cin >> h >> w;
h += 2;
w += 2;
ostrov = new int*[w];
for (int i = 0; i < w; i++) {
ostrov[i] = new int[h];
for (int j = 0; j < h; j++)
ostrov[i][j] = 1;
}
int n;
cin >> n;
int color = 1;
int act = 0; //actual color
int prev = 0; //last color
for (int i = 0; i < n; i++) {
color++;
suc = color % 2;
prev = (color - 1) % 2;
int x, y;
cin >> x >> y;
if (ostrov[y][x] == act) {
cout << (w * h) - area << endl;
color--;
continue;
}
area = 0;
ostrov[y][x] = 5;
floodFillScanlineStack(0, 0, act, prev);
cout << (w * h) - area << endl;
}
}
编辑:
现在我意识到这个封闭区域不需要是矩形或正方形,它可以是多边形。此外,地图中可能还有更多的多边形。在我得到一部分墙的坐标后,我必须告诉他们:
1.)如果它形成一些多边形(封闭区域) - 如果是,那么在我必须添加之前未封闭的大区域是封闭的;
2.)如果它没有形成一些多边形并且它不在已经封闭的区域中,则添加到封闭区域1(因为这个地图水箱不能泛滥);
3.)如果它位于已经封闭的区域,则不添加任何内容,因为它不包含任何其他区域。
答案 0 :(得分:0)
这是一个可能有所帮助的想法。封闭更多空间而不是墙壁本身占据的唯一方法是创建某种封闭的路径。考虑从放置新墙段完成的点开始执行4向洪水填充的内容。它(通过微小的修改)允许您检测一条封闭的路径 - 并提醒您有一些非墙壁空间可以防止洪水泛滥。
真的,这可能会更好地被认为是一种深度优先搜索,你正在寻找第二次出现的原始点。你会想要继续搜索,因为一个新的墙段可能会完成一些封闭的路径(实际上,这可能不是真的;要成为防止8向填埋的障碍,最后一块所需的墙只会属于一个形成一个新的循环(除非你把它放在已形成的其他形状内)。
一旦检测到闭环,你只需要用其他颜色填充内部正方形(如果白色是空白而黑色是墙壁,可能是红色或其他东西);任何未来的红色方块墙都无济于事。弄清楚如何填充内部现在是唯一的问题 - 这是一个简单的问题。只需检查新墙周围的方块即可。找到正方形后,水平或垂直扫描(取决于您从点开始的方向)并查看是否再次越过路径。如果你经过了奇数次,你就会在形状内;如果是偶数次,你就在外面。新墙体周围会有一些空白区域完成一条封闭的路径,否则任何环路都必须已经关闭。
呼。我认为这应该比每次迭代的洪水填充快得多。仍然没有在公园散步,但有些值得深思。