C++ 防洪-洪水填充还是有更好的方法?

C++ 防洪-洪水填充还是有更好的方法?,c++,algorithm,optimization,flood-fill,C++,Algorithm,Optimization,Flood Fill,我在做练习和提高技能的任务,但我很难解决这个问题: 你有一张w*h大小的地图。这张地图的每个盒子上都有wallflood 保护还是什么都没有。水可以朝八个方向流动 在地图边缘的每个框上。当然,水不能泛滥 用于防洪的箱子。你会得到地图的大小, 墙的数量和每面墙的位置 开始时,地图是空的。您的任务是在放置每个 这堵墙告诉我们有多大的区域可以防水 所以,我有代码可以工作。但是太慢了。限制为:地图的大小w和h 1≤w、 h≤2000 墙数:n 1≤N≤w×h 我尝试了8路泛光填充算法,然后将其改进为8路

我在做练习和提高技能的任务,但我很难解决这个问题:

你有一张w*h大小的地图。这张地图的每个盒子上都有wallflood 保护还是什么都没有。水可以朝八个方向流动 在地图边缘的每个框上。当然,水不能泛滥 用于防洪的箱子。你会得到地图的大小, 墙的数量和每面墙的位置

开始时,地图是空的。您的任务是在放置每个 这堵墙告诉我们有多大的区域可以防水

所以,我有代码可以工作。但是太慢了。限制为:地图的大小w和h 1≤w、 h≤2000 墙数:n 1≤N≤w×h

我尝试了8路泛光填充算法,然后将其改进为8路扫描线填充。它太慢了,有一半的测试条目没有时间了。如果你愿意,我会发布我的代码。 所以我的问题是:如何改进我的算法,使其更快?还是我选择的算法完全错了,还有更好的方法?谢谢你的建议

测试条目:

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.如果它位于已封闭的区域内,请不要添加任何内容,因为它不会再封闭任何区域。

这里有一个想法可能会有所帮助。封闭比墙本身占用的空间更多的唯一方法是创建某种封闭路径。考虑在一个新的墙段被放置的地方开始执行4路洪水填充。它只需稍加修改,就可以让您检测到一条封闭的路径,并提醒您注意,有一些非墙壁空间是安全的,不会被洪水淹没

事实上,这可能是一种深度优先的搜索方式,您可以在其中查找第二次出现的原始点。您需要继续搜索,因为一个新的墙段可能会完成许多闭合路径,实际上,这可能不是真的;为了防止8向洪水泛滥,最后需要的一块墙将只属于一个新形成的环,除非您将其放置在其他已形成的形状内

一旦你检测到一个闭合环,你只需要用其他颜色填充内部正方形,如果白色是空白,黑色是墙壁,可能是红色或其他什么;红色广场上的任何未来墙都不会有帮助。弄清楚如何填充内部是现在唯一的问题——这是一个简单的问题。只需检查新墙周围的方格。一旦你找到一个正方形,根据你的方向水平或垂直扫描,看你是否再次穿过这条路径。如果你穿过奇数次,你就在形状里面;如果偶数次,你就在外面。新墙板周围将有一些空白,以完成闭合路径,否则,任何循环都必须已经闭合


呸。我认为这应该比每次迭代时的洪水填充速度快得多。仍然不是在公园散步,而是一些值得思考的东西。

嗯,您没有提供代码,因此无法提供帮助。此外,这听起来似乎更适合@Let_Me_Be,除了主题中最重要的一个标准,就是要审查的代码必须包含在问题中。我不认为这更适合codereview,因为我想知道如何比使用Floodfill更快地解决任务,不检讨我的code@JozefBugoš你在哪里找到这个问题?如果边界是一道屏障,洪水从0,0开始,你不能在1,0,0,1和1,1处用三堵墙隔离0,0,并防止洪水淹没地图的剩余Ow*h吗?如果你只有两个或更少的墙可以放置,你不能阻止任何非墙区域被淹没。谢谢!我会看一看,试试看。我会注意到你的。@Patrik87我在考虑你的想法,我的结论是,检测周期的原始DFS由于一个原因无法工作,它只检测一个周期,而可能有更长的周期-我需要最长的周期。如果我找到了,我会检查禁区和f在哪里 我会用我的洪水填充算法,但我需要最长的周期。我发现了这个,但我不理解Alex Kemper给出的代码。你能给我解释一下,还是把它重写到C++?另外,我只需要检查一个顶点的循环