【问题标题】:Counting Rooms While Knowing Where Walls Are在知道墙壁在哪里的同时计算房间
【发布时间】:2016-10-18 11:23:15
【问题描述】:

这个问题是关于 C++ builder 6 的代码。赏金有兴趣使用标准 C++ 算法来解决给定标准化输入的问题(有关更多信息,请参阅this。)

txt 文件也代表我在数组中的数据:

1101 0110 1101 0110 1100 0101 0110
1110 1001 0110 1011 1010 1111 1010
1000 0101 0011 1110 1011 1110 1010
1011 1101 0101 0001 0101 0011 1011

txt的解释:
txt 文件中的数字是房间墙壁的 4 位表示,设置位表示墙壁。墙位按顺时针顺序排列,最重要的位是西墙。例如,1101 代表一个房间,其中:

  • 最高有效位置的设置位表示西边的墙
  • 下一个最高有效位置的设置位表示北面是一堵墙
  • 未设置的位表示东边没有墙
  • 最低有效位置的设置位表示南面的墙

鉴于:

  1. 房间的外墙总是有一堵墙
  2. 内墙将始终在两个房间中表示(在示例中,因为 (1, 1) 的房间是 1101 它包含南面的墙,位于 (1, 2) 的房间1110 必须在北边有一堵墙
  3. 永远不会超过numeric_limits<int>::max() 个房间

我是was asked to post my code 所以这里是:
我已尝试解决此问题,但遇到 EAAccessviolation 有人可以告诉我我做错了什么吗?

  int rn=0,z=0, global=0,coord[15],c[411],b1[411];

void peruse ( int i, int j,int* bb)
{
bool top=false,bottom=false,right=false,left=false;
//truth checks

if (bb[i*m+j]<1000)  left=true;

if (bb[i*m+j]<100)   top=true; else if (bb[i*m+j]-1000<100)   top=true;

if (bb[i*m+j]<10)    right=true; else
if ( (bb[i*m+j]-100<10) || (bb[i*m+j]-1000<10) || (bb[i*m+j]-100<10) ) right=true;

if (bb[i*m+j]<1)   bottom=true; else
if ( (bb[i*m+j]-10<1) || (bb[i*m+j]-100<1) || (bb[i*m+j]-1000<1) ||(bb[i*m+j]-100<1))
bottom=true;
//marc

if  (left)
{
c[i*m+j]=c[i*m+j]+1000; // EAaccessViolation i dont know why.....
peruse(i,j-1,c);
}
if (top)
{
c[i*m+j]=c[i*m+j]+100;
peruse(i-1,j,c);
}
if (right)
{
c[i*m+j]=c[i*m+j]+10;
peruse(i,j+1,c);
}
if (bottom)
{
c[i*m+j]=c[i*m+j]+1;
peruse(i+1,i,c);
}
 if ( !(left) && !(top) && !(right) && !(bottom) )
 {
  bb[411]++;



 }
}


void __fastcall TForm1::Button7Click(TObject *Sender)
{
b1[411]=0;

 for(int i=0;i<n;i++)
    for (int j=0;j<m;j++)
          {
           b1[i*m+j]=b[i][j];
           c[i*m+j]=b[i][j];
          }
  peruse (1,1,b1);

 ShowMessage("Nr. "+IntToStr(b1[411]) );
}

【问题讨论】:

  • "请把代码写给我"
  • 请给我一个想法,或者如果可能的话,给我一个例子。谢谢。
  • @JonathanMee 我明白了。看到尼基塔琼恩在说什么了吗?您有机会尝试并在此处发布“您的代码”以及您的为什么何时失败了,你期待什么,我们会帮助你。如果你自己不做,没有人会为你做
  • 好的,我会尝试检查零,直到它击中一个并将这些零转换为零,但它是一种更多用于海战的算法,所以我不确定它是否可以在这里工作。谢谢。
  • @NikitaCebotari 您应该阅读有关不相交集数据结构的信息。这可能会有所帮助。

标签: c++ algorithm variable-assignment c++builder


【解决方案1】:

老实说,我真的很想尝试解决这个问题。所以我要说你已经在这方面做出了勇敢的努力,然后继续向你展示如何去做。我将假设您可以提供以下算法:

  1. 以二进制读取的输入数字(因此“1101”将被读取为十进制数“13”)
  2. const vector&lt;char&gt; rooms 中的所有这些数字
  3. 每行“房间”的宽度可以输入size_t width(必须在所有行中保持一致,我们必须使用矩形房间)
  4. “房间”的所有外部“墙”都会有“墙”
  5. 少于numeric_limits&lt;int&gt;::max()“房间”

我们将使用vector&lt;int&gt; temp标记每个房间,我们将构造它的大小为rooms并将每个标签初始化为0。int result将用于标记房间,并将初始化为0 . 但是因为替换较小的标签时不会减少所有房间标签,所以size(set&lt;int&gt;(cbegin(temp), cend(temp))) 将用于查找最终的标签计数。

我们的解决方案将围绕一个功能构建,该功能包含两个没有墙壁的“房间”;这样:

  1. 一个房间尚未标记,在这种情况下,它将采用另一个房间的标签
  2. 两个房间共享一个标签,在这种情况下不会发生任何操作
  3. 一个房间标签较小,在这种情况下,较大标签的所有房间都将分配较小的标签

关于此函数的重要说明,我使用一元加号运算符从 L 值 int&amp; 构造 R 值 int 更多信息 here。更清晰的解决方案可能是使用static_cast&lt;int&gt;,但由于某种原因,Visual Studio 2015 无法按预期工作更多信息here

void generate(vector<int>& temp, int& target, const size_t width, const size_t i) {
    const auto replacement = temp[i];

    if (target > replacement) {
        replace(begin(temp), next(begin(temp), min(size(temp), i + width - 1)), target, replacement);
    } else {
        target = replacement;
    }
}

使用此代码,我们能够:

for (size_t i = 0U; i < size(rooms); ++i) {
    const auto toWest = (rooms[i] & 0b1000) == 0;
    const auto toNorth = (rooms[i] & 0b100) == 0;
    const auto toEast = (rooms[i] & 0b10) == 0;
    const auto toSouth = (rooms[i] & 0b1) == 0;
    const auto west = toWest && temp[i - 1] != 0 ? temp[i - 1] : numeric_limits<int>::max();
    const auto north = toNorth && temp[i - width] != 0 ? temp[i - width] : numeric_limits<int>::max();
    const auto east = toEast && temp[i + 1] != 0 ? temp[i + 1] : numeric_limits<int>::max();

    temp[i] = min({ temp[i] != 0 ? temp[i] : numeric_limits<int>::max(), result + 1, west, north, east });

    if (temp[i] == result + 1) ++result;

    if (toWest) generate(temp, temp[i - 1], width, i);
    if (toNorth) generate(temp, temp[i - width], width, i);
    if (toEast) generate(temp, temp[i + 1], width, i);
    if (toSouth) temp[i + width] = temp[i];
}

Live Example

【讨论】:

    【解决方案2】:

    这是一个在图中求connected components总数的典型问题。

    让我通过关注以下几点来帮助您形象化类比,请记住我们在这里处理的是undirected graphs

    1。在图中,我们有各种顶点,如果两个顶点之间有边,则称它们彼此相邻。 就像你的城堡一样,如果一个单元可以通向另一个单元,那么两个单元彼此相邻。

    2。在一个图中,如果两个顶点之间有一条使用边的路径,那么我们有两个顶点属于同一个连通分量。 就像你的城堡一样,两个牢房属于同一个房间号,如果一个牢房可以通过一条牢房的路径通向另一个。

    3。在图中,我们有连接组件,因此连接组件由顶点组成,因此连接组件的每两个顶点之间都有一条路径。就像我们有房间的城堡一样,每两个单元格同一个房间之间有一条细胞路径。

    现在,如果您仍然想知道如何构建图表,那么这很容易。

    顶点的数量将是NxM对于大小为 N 行 M 列的城堡),它等于单元的数量。

    只需按顺序对单元格编号,如果两个单元格相邻,cell a(meaning vertex a)cell b(vertex b) 之间就有一条边。

    现在可以通过在您构建的图表上应用 bfs 或 dfs 算法轻松计算房间总数。

    算法在我提供的第一个链接中描述。

    【讨论】:

    • 您的回答非常有道理,但我的大脑无法理解我将如何准确地根据我以前从未见过的图表制作图表。谢谢。
    • @NikitaCebotari 我只是无法用一个答案来解释数据结构图。维基百科和 youtube 是一个不错的起点。
    • @NikitaCebotari 你现在学习它们也很好,因为它们是大多数问题的组成部分,可以使用算法轻松解决。
    【解决方案3】:

    一种简单的方法是创建一个具有房间大小的数组并用“0”初始化它。 之后,您应该遍历数组。如果找到“0”,则从该点开始 BFS,并将数组结果着色为当前数字。

    关于 BFS 的信息

    BFS 必须查看直接邻居并检查此数组内是否有“0”。如果这是真的,BFS 必须检查两个盒子之间是否没有墙。如果中间没有墙,则使用当前颜色(开头为 1)为该框着色,并在新框上调用 BFS。

    当房间完全着色时,BFS 会自动停止。然后全局颜色将增加 1,循环继续并寻找下一个“0”。

    在循环之后,房间的数量存储在全局颜色值中。

    这个算法在 O(n) 中工作

    小例子:

    //CountingRooms.h
    #include <vector>
    
    class CountingRooms
    {
    public:
        CountingRooms();
        ~CountingRooms();
        int Count();
    
        void TestFill();
        void Print();
    private:
        struct Point
        {
            int x = 0;
            int y = 0;
        };
        unsigned int* m_arrFieldColors = nullptr;
        unsigned int* m_arrFieldWalls = nullptr;
        int m_nSizeX = 0;
        int m_nSizeY = 0;
        int m_nCurrentColor = 0;
    
        unsigned int GetValue(unsigned int* field, int x, int y);
        void SetValue(unsigned int* field, int x, int y, unsigned int value);
    
        bool CanPass(int x1, int y1, int x2, int y2);
        void DFS(int posX, int posY);
        bool IsInsideArray(int x1, int y1); 
    };
    
    
    
    //CountingRooms.cpp
    #include "stdafx.h"
    #include "CountingRooms.h"
    #include <iostream>
    
    CountingRooms::CountingRooms()
    {
    }
    
    
    CountingRooms::~CountingRooms()
    {
        if (m_arrFieldColors)
        {
            delete[]m_arrFieldColors;
        }
        if (m_arrFieldWalls)
        {
            delete[]m_arrFieldWalls;
        }
    }
    
    bool CountingRooms::IsInsideArray(int x, int y)
    {
        return x >= 0 && y >= 0 && x < m_nSizeX && y < m_nSizeY;
    }
    
    bool CountingRooms::CanPass(int x1, int y1, int x2, int y2)
    {
        if (IsInsideArray(x1, y1) && IsInsideArray(x2, y2)) //inside the array range
        {
            if (x2 - x1 == 1 && y2 - y1 == 0) // right
            {
                if (!(GetValue(m_arrFieldWalls, x1, y1) & 2) && !(GetValue(m_arrFieldWalls, x2, y2) & 8)) { return true; }
            }
            if (x2 - x1 == 0 && y2 - y1 == -1) // up
            {
                if (!(GetValue(m_arrFieldWalls, x1, y1) & 4) && !(GetValue(m_arrFieldWalls, x2, y2) & 1)) { return true; }
            }
            if (x2 - x1 == -1 && y2 - y1 == 0) // left
            {
                if (!(GetValue(m_arrFieldWalls, x1, y1) & 8) && !(GetValue(m_arrFieldWalls, x2, y2) & 2)) { return true; }
            }
            if (x2 - x1 == 0 && y2 - y1 == 1) // down
            {
                if (!(GetValue(m_arrFieldWalls, x1, y1) & 1) && !(GetValue(m_arrFieldWalls, x2, y2) & 4)) { return true; }
            }
        }
        return false;
    }
    
    void CountingRooms::DFS(int posX, int posY)
    {
        if (GetValue(m_arrFieldColors, posX, posY)) // check if the field is already colored
        {
            return;
        }
        Point sStart;
        sStart.x = posX;
        sStart.y = posY;
        std::vector<Point> vecList;
        vecList.push_back(sStart);
    
        m_nCurrentColor++;
    
        while (vecList.size()) // as long as something is inside the list
        {
            Point sTemp = vecList[vecList.size()-1]; //get out the last element
            vecList.pop_back();
    
            if (IsInsideArray(sTemp.x, sTemp.y))
            {
                if (!GetValue(m_arrFieldColors, sTemp.x, sTemp.y)) // is field not colored
                {
                    SetValue(m_arrFieldColors, sTemp.x, sTemp.y, m_nCurrentColor);
    
                    if (CanPass(sTemp.x, sTemp.y, sTemp.x + 1, sTemp.y)) /* right*/
                    {
                        Point newPoint;
                        newPoint.x = sTemp.x + 1;
                        newPoint.y = sTemp.y;
                        vecList.push_back(newPoint);
                    }
                    if (CanPass(sTemp.x, sTemp.y, sTemp.x - 1, sTemp.y)) /* left*/
                    {
                        Point newPoint;
                        newPoint.x = sTemp.x - 1;
                        newPoint.y = sTemp.y;
                        vecList.push_back(newPoint);
                    }
                    if (CanPass(sTemp.x, sTemp.y, sTemp.x, sTemp.y - 1)) /* up*/
                    {
                        Point newPoint;
                        newPoint.x = sTemp.x;
                        newPoint.y = sTemp.y - 1;
                        vecList.push_back(newPoint);
                    }
                    if (CanPass(sTemp.x, sTemp.y, sTemp.x, sTemp.y + 1)) /* down*/
                    {
                        Point newPoint;
                        newPoint.x = sTemp.x;
                        newPoint.y = sTemp.y + 1;
                        vecList.push_back(newPoint);
                    }
    
                }
            }
        }
    }
    
    int CountingRooms::Count()
    {
        m_nCurrentColor = 0;
        for (int i = 0; i < m_nSizeY; ++i)
        {
            for (int j = 0; j < m_nSizeX; ++j)
            {
                DFS(j, i);
            }
        }
        return m_nCurrentColor;
    }
    
    void CountingRooms::TestFill()
    {
        m_arrFieldWalls = new unsigned int[42]{13, 6,13, 6,12, 5, 6,
                                               14, 9, 6,11,10,15,10,
                                                8, 5, 3,14,11,14,10,
                                               11,13, 5, 1, 5, 3,11};
        m_arrFieldColors = new unsigned int[42];
        for (int i = 0; i < 42;i++)
        {
            m_arrFieldColors[i] = 0;
        }
        m_nSizeX = 7;
        m_nSizeY = 4;
    
    }
    
    unsigned int CountingRooms::GetValue(unsigned int* field, int x, int y)
    {
        if (IsInsideArray(x, y))
        {
            return field[x + m_nSizeX*y];
        }
        return -1;
    }
    
    void CountingRooms::SetValue(unsigned int* field, int x, int y, unsigned int value)
    {
        if (IsInsideArray(x, y))
        {
            field[x + m_nSizeX*y] = value;
        }
    }
    
    void CountingRooms::Print()
    {
        std::cout << "Walls:" << std::endl;
        for (int j = 0; j < m_nSizeY;++j)
        {
            for (int i = 0; i < m_nSizeX;++i)
            {
                std::cout << GetValue(m_arrFieldWalls, i, j) << "\t";
            }
            std::cout << std::endl;
        }
        std::cout << std::endl<<"Colors:" << std::endl;
    
        for (int j = 0; j < m_nSizeY;++j)
        {
            for (int i = 0; i < m_nSizeX;++i)
            {
                std::cout << GetValue(m_arrFieldColors, i, j) << "\t";
            }
            std::cout << std::endl;
        }
    }
    
    
    
    
    
    
    
    //main.cpp
    #include "stdafx.h"
    #include <iostream>
    #include "CountingRooms.h"
    
    int main()
    {
        CountingRooms cr;
        cr.TestFill();
        std::cout<<"There are "<<cr.Count()<<" rooms"<<std::endl;
        cr.Print();
    
        char key = 0;
        std::cin >> key;
        return 0;
    }
    

    顺便说一句:BFS 被 DFS 取代,但两者都在工作。

    输出

    【讨论】:

    • 它不是一个简单的 BFS 算法的原因是我们经常需要重新着色房间。考虑输入集:const vector&lt;char&gt; rooms = {0b1110, 0b1110, 0b1001, 0b0011}const size_t width = 2U 在这种情况下,第二个房间最初将使用与第一个房间不同的颜色着色,但随后应使用与第一个房间相同的颜色重新着色。我认为在 O(n) 中没有办法做到这一点,但我希望看到实际的编码尝试!
    • 嘿,我很感激在这方面的努力......我很遗憾看到你和我似乎是唯一对这个问题感兴趣的人。因此,如果你能获得解决问题的代码(如果你能击败我的运行时,我会很乐意给你至少一个 +1有大量的云编译器可供您使用。随意 fork my code 并比较我们的输出。
    • 我 100% 确定,这个想法还在工作。也许有一些sintax错误,但它们应该很容易修复。唯一要做的就是将这些位放入一个整数数组并运行此代码。
    • 是的,您的代码中肯定存在语法问题。但在大多数情况下,它们看起来很简单,所以我同意它们不应该很难修复。我认为最简单的方法是分叉my code 并进行比较。但是,如果您遇到问题,您可以随时访问ideone.com 并从头开始。
    • @JonathanMee 抱歉,我现在在上班……我回家后会这样做
    【解决方案4】:

    您的代码很少有问题禁止从第三方进行正确调试,例如有关其工作原理的信息不足,未定义的变量 (m,n,b) 数组溢出(大量访问 [411] 而大小仅为 411 而不是412) 从一开始就阻止任何人尝试(让人怀疑代码是否真的有用并且值得花时间花时间)。我也很好奇,所以我在 BDS2006 Turbo C++BCB6 的继任者,所以这段代码也应该在那里工作)中为这个任务做了简单的未优化尝试。

    [rooms.h]

    //---------------------------------------------------------------------------
    #ifndef _rooms_h
    #define _rooms_h
    //---------------------------------------------------------------------------
    class rooms
        {
    public:
        // variables
        int xs,ys;          // map resolution
        DWORD **map;        // map[xs][ys]
    
        enum
            {
            _W=8,
            _N=4,
            _E=2,
            _S=1
            };
    
        // internals
        rooms();
        ~rooms();
        void _free();                                       // release map memory
    
        // inteface
        void resize(int _xs,int _ys);                       // realloc map to new resolution
        void set(AnsiString txt);                           // copy txt to map
        void draw(TCanvas *scr,int x,int y,int sz);         // draw map on Canvas at (x,y) with grid size sz
        int  count();                                       // count rooms
        };
    //---------------------------------------------------------------------------
         rooms::rooms()     { map=NULL; xs=0; ys=0; }
         rooms::~rooms()    { _free(); }
    //---------------------------------------------------------------------------
    void rooms::_free()
        {
        if (map)
            {
            for (int x=0;x<xs;x++)
             if (map[x])
              delete[] map[x];
            delete[] map;
            }
        map=NULL; xs=0; ys=0;
        }
    //---------------------------------------------------------------------------
    void rooms::resize(int _xs,int _ys)
        {
        if ((xs==_xs)&&(ys==_ys)) return;
        _free();
        xs=_xs; ys=_ys;
        map=new DWORD*[xs];
        for (int x=0;x<xs;x++)
         map[x]=new DWORD[ys];
        }
    //---------------------------------------------------------------------------
    void rooms::set(AnsiString txt)
        {
        int i,l,x,y,n0,n1;
        l=txt.Length(); if (!l) return;
        // count eof lines (ys)
        for (y=0,i=1;i<=l;i++)
         if ((txt[i]==13)||(txt[i]==10))
            {
            y++;
            if (i<l)
             if ((txt[i+1]==13)||(txt[i+1]==10)) i++;
            }
         if ((txt[l]!=13)&&(txt[l]!=10)) y++;   // handle missing last eof
        // count numbers per line (xs)
        for (n1=0,x=0,i=1;i<=l;i++)
            {
            n0=n1; n1=0;
            if ((txt[i]=='0')||(txt[i]=='1')) n1=1;
            if ((txt[i+1]==13)||(txt[i+1]==10)) break;
            if ((!n0)&&(n1)) x++;
            }
        // copy data
        resize(x,y);
        for (x=0,y=0,i=1;i<=l;)
            {
            // skip spaces
            while ((i<=l)&&(txt[i]!='0')&&(txt[i]!='1')) i++;
            // read 4 bit bin number
            n0=  0; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
            n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
            n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
            n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
            map[x][y]=n0;
            x++; if (x>=xs) { x=0; y++; if (y>=ys) break; }
            }
        // clear the rest in case of error in data
        if ((y<ys)&&(x<xs)) for (;;)
            {
            map[x][y]=0;
            x++; if (x>=xs) { x=0; y++; if (y>=ys) break; }
            }
        }
    //---------------------------------------------------------------------------
    void rooms::draw(TCanvas *scr,int x0,int y0,int sz)
        {
        int x,y,xx,yy;
        DWORD a;
    
        scr->Brush->Color=clDkGray;
        scr->Brush->Style=bsSolid;
        scr->FillRect(Rect(x0,y0,x0+xs*sz,y0+ys*sz));
    
        scr->Pen->Color=clBlue;
        scr->Pen->Width=5;
        scr->Font->Color=clBlack;
        scr->Brush->Style=bsClear;
        for (xx=x0,x=0;x<xs;x++,xx+=sz)
         for (yy=y0,y=0;y<ys;y++,yy+=sz)
            {
            a=map[x][y]&15;
            if (DWORD(a&_W)) { scr->MoveTo(xx   ,yy   ); scr->LineTo(xx   ,yy+sz); }
            if (DWORD(a&_N)) { scr->MoveTo(xx   ,yy   ); scr->LineTo(xx+sz,yy   ); }
            if (DWORD(a&_E)) { scr->MoveTo(xx+sz,yy   ); scr->LineTo(xx+sz,yy+sz); }
            if (DWORD(a&_S)) { scr->MoveTo(xx   ,yy+sz); scr->LineTo(xx+sz,yy+sz); }
            scr->TextOutA(xx+(sz>>1),yy+(sz>>1),map[x][y]>>4);
            }
        scr->Brush->Style=bsSolid;
        scr->Pen->Width=1;
        }
    //---------------------------------------------------------------------------
    int  rooms::count()
        {
        int x,y,i,i0,i1,w0,w1,n,e;
        // each block is a separate room
        for (n=0,x=0;x<xs;x++)
         for (y=0;y<ys;y++,n+=16)
            {
            map[x][y]&=  0x0000000F;    // low 4 bits are walls
            map[x][y]|=n&0xFFFFFFF0;    // rest is room index
            } n>>=4;
        // reindex all indexes i0 to i1
        #define map_reindex(i0,i1)                      \
            for (x=0;x<xs;x++)                          \
             for (y=0;y<ys;y++)                         \
              if (DWORD(map[x][y]&0xFFFFFFF0)==i0)      \
                {                                       \
                map[x][y]&=   0x0000000F;               \
                map[x][y]|=i1&0xFFFFFFF0;               \
                }
        // loop until no change has occured
        for (e=1;e;)
            {
            e=0;
            // merge columns
            for (x=0;x<xs;x++)
             for (y=1;y<ys;y++)
                {
                w0=map[x][y-1]&0x0000000F;
                i0=map[x][y-1]&0xFFFFFFF0;
                w1=map[x][y  ]&0x0000000F;
                i1=map[x][y  ]&0xFFFFFFF0;
                if ((i0!=i1)&&(DWORD(w0&_S)==0)&&(DWORD(w1&_N)==0)) { map_reindex(i0,i1); n--; e=1; }
                }
            // merge rows
            for (y=0;y<ys;y++)
             for (x=1;x<xs;x++)
                {
                w0=map[x-1][y]&0x0000000F;
                i0=map[x-1][y]&0xFFFFFFF0;
                w1=map[x  ][y]&0x0000000F;
                i1=map[x  ][y]&0xFFFFFFF0;
                if ((i0!=i1)&&(DWORD(w0&_E)==0)&&(DWORD(w1&_W)==0)) { map_reindex(i0,i1); n--; e=1; }
                }
            }           
    
        return n;
        #undef map_reindex
        }
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    [不带任何组件的单窗口VCL Form app C++源码]

    //$$---- Form CPP ----
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    #include "Unit1.h"
    #include "rooms.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    Graphics::TBitmap *bmp; // back buffer
    int xs,ys;              // actual window resolution
    rooms map;              // map of rooms
    //---------------------------------------------------------------------------
    void draw()
        {
        int x,y,sz;
        // clear bachground
        bmp->Canvas->Brush->Color=clBlack;
        bmp->Canvas->Brush->Style=bsSolid;
        bmp->Canvas->FillRect(Rect(0,0,xs,ys));
        // compute grid size
        x=(xs-20)/map.xs; sz=x;
        y=(ys-20)/map.ys; if (x>y) sz=y;
        // and map position so it is centered
        x=(xs-(sz*map.xs))>>1;
        y=(ys-(sz*map.ys))>>1;
        // render to backbuffer (avoid flickering)
        map.draw(bmp->Canvas,x,y,sz);
        // render backbuffer to window
        Form1->Canvas->Draw(0,0,bmp);
        }
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
        {
        // init backbuffer
        bmp=new Graphics::TBitmap;
        bmp->HandleType=bmDIB;
        bmp->PixelFormat=pf32bit;
        // init map
        map.set("1101 0110 1101 0110 1100 0101 0110\r\n1110 1001 0110 1011 1010 1111 1010\r\n1000 0101 0011 1110 1011 1110 1010\r\n1011 1101 0101 0001 0101 0011 1011\r\n");
        Caption=map.count();    // result count is in the Window Caption
        }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormDestroy(TObject *Sender) { delete bmp; }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormPaint(TObject *Sender) { draw(); }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormResize(TObject *Sender)
        {
        // get actual window size
        xs=ClientWidth;
        ys=ClientHeight;
        // resize backbufer and force redraw
        bmp->SetSize(xs,ys);
        draw();
        }
    //---------------------------------------------------------------------------
    

    我的求解器背后的想法很简单:

    1. ID每个网格单元按不同的房间号

      记住单元格计数为n

    2. 合并所有相邻的房间,它们之间没有任何墙

      所以循环遍历所有房间,如果任何相邻的单元格没有墙壁并且有不同的房间ID 重新索引其房间号,以便展位房间有相同的房间。同时减少房间计数器n

    3. 循环 #2 直到不发生重新索引

    [备注]

    不要忘记在您的 IDE 中创建适当的事件,而不是仅仅复制代码,否则它将无法工作。

    【讨论】:

    • 这确实解决了问题+1。 I don't really care whether you do it in C++ builder 6 for the bounty,所以我在这里将您的代码提取到纯 C++ 文件中:ideone.com/ecd0tU 此算法似乎与我自己的解决方案相似,尽管可能是二维数组影响了您的速度。
    • @JonathanMee 速度被重新索引宏杀死,因为它循环遍历整个数组而不是填充或索引行合并。而且还有 3 倍的数组访问,这是真正需要的,只是为了让代码更易于理解
    • 是的,但我的意思是我的代码中的replace 也必须经过并重新标记,但我想我只需要重新标记我已经标记的房间,而不是所有内容。所以你是对的,这可能是这里的放缓。我正在修改一个链表或类似的东西,我可以用它来将节点分配给一个标签,而不是遍历所有标签。如果加速显着,我可能会给出答案。
    • @JonathanMee ID 列表不是解决此问题的好方法...通过记住边界框并仅使用其区域,我得到了更好的结果...合并后您只需合并边界框,这将加快速度相当。但是对于快速图像分割,我使用线段而不是单个像素(单元格)。它将堆栈/堆垃圾和需求降低到很小的一部分...对于大分辨率,您可以递归地划分和征服地图以降低复杂性...
    • 我再次对渲染不感兴趣。我想知道计算房间需要多长时间,IO 通常会掩盖任何计算时间,这是我感兴趣的全部。我正在测试 the extraction of that algorithm 反对我的。而那个就是我说的比我的慢。
    【解决方案5】:

    不相交集以及Union-Find非常适合连接组件的问题。我不是使用图表,而是跟踪不同的不相交的地砖集。每个集合都有自己的代表图块,用于唯一标识该集合。

    您可以阅读有关 Union-Find here 的更多信息。

    算法很简单:

    1. 对于每个图块,如果没有公共墙,则处理其相邻的图块。这是使用两个图块的简单union() 完成的。当我们完成后,每个图块将属于一个不相交的集合(每个集合代表一个连接空间房间)。附件代码中parent[]存放了代表元素。

      • 完成后,标准化每个图块的代表元素。
    2. 计算唯一代表元素的数量。这是不相交集的数量(因此是连接空间房间的数量)。

    一些额外的观察:

    • 在任何时候,您只需要处理北墙和西墙。 (为什么?证明留给读者。)

      那是因为对于一个瓷砖a[i][j],它的南墙和东墙可以分别被瓷砖a[i+1][j]a[i][j+1]处理。

    • 为什么我们需要标准化每个图块的代表?原因是某些集合最终可能包含两个或多个具有代表性的元素。我们递归地找到父元素以获得整个集合的唯一代表。 (如果您想对此进行测试,请从附加代码中注释掉以下行:

      // normalise each tile
      parent[i] = findSet(parent[i]);
      
    • 所附代码使用了一个特殊版本的 Union-Find,有两个启发式:

      • rank 联合(由 ranks[] 证明)
      • 路径压缩

    附上代码:Live Example

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      • 1970-01-01
      • 2012-06-29
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 2015-09-30
      相关资源
      最近更新 更多