Counting the numbers knowing where the walls are

This question relates to code for C ++ builder 6. The bounty is interested in the standard C ++ algorithm to solve the problem with standardized input (see this for more information.)

Castle

A txt file that also represents the data that I have in the array:

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 explanation:
The numbers from the txt file are a 4-bit representation of the walls in the room, and the set bit is a wall. Wall bits are in clockwise order, starting with the most significant bit, which is the western wall. For example, 1101 represents a room where:

  • The set bit in the most significant position points to the wall to the West.
  • The set bit in the next most significant position points to the wall to the north
  • An uncorrected bit indicates the absence of a wall to the east.
  • A set bit in the least significant position points to the wall to the south.

Given:

  • The outer walls of the rooms will always have a wall
  • The inner walls will always be expressed in both rooms (in the example, since room (1, 1) is 1101, it contains a wall to the south, room in (1, 2) 1110 should contain a wall North
  • There will never be more numeric_limits<int>::max() rooms

I was asked to publish my code , so here:
I tried to solve this, but I get EAAccessviolation, can someone tell me what I'm doing wrong?

  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]) ); } 
+5
source share
5 answers

This is a typical problem of finding the total number of related components in a graph.

Let me help you visualize the analogy by focusing on the following points, bearing in mind that we are dealing with undirected graphs here.

1 . In the graph, we have different vertices, and two vertices are called adjacent to each other if there is an edge between them. Like your castle, where two cells are adjacent to each other, if one cell can lead to another cell.

2 . In a graph, we have two vertices that belong to the same connected component if there is a path between two vertices that use edges. Just like your castle, where two cells belong to the same room number, if one cell can follow the path of the cells, it can lead to another.

3 . On the graph, we have connected components, so that the connected component consists of vertices, so that each of the two vertices of the connected component has a path between them. Just like your castle, where we have rooms, so each of the two cells from the same room has a path of cells between them.

Now, if you are still wondering how to build a chart, well its easy.

The number of vertices will be NxM (for a castle with a size of N rows and columns M), which is equal to the number of cells.

Just number the cells sequentially, and there is an edge between cell a(meaning vertex a) and cell b(vertex b) if both cells are adjacent.

Now the total number of rooms can be easily calculated using the bfs or dfs algorithm on your chart that you create.

The algorithm is described in the first link I provided.

+11
source

So honestly, I just really wanted to try to solve it. So I’m going to say that you made a brave effort in this, and just go ahead and show how to do it. I am going to suggest that you can provide an algorithm:

  • Input numbers are read in binary format (so "1101" will be considered the decimal number "13")
  • All these numbers in const vector<char> rooms
  • The width of each row of "rooms" can be placed in size_t width (which should be consistent across all lines, we must work with the rectangle of rooms)
  • All external "walls" of the "rooms" will have a "wall"
  • There are fewer numeric_limits<int>::max() "rooms"

We will use vector<int> temp to designate each room, we will build it with the size of rooms and initialize each label to 0. int result will be used to designate rooms and will be initialized to 0 But since all room labels will not decrease when replacing a smaller label, size(set<int>(cbegin(temp), cend(temp))) will be used to determine the final number of labels.

Our solution will be built around a function taken in 2 "rooms" between which there is no wall; such that either:

  • One room has not yet been marked, in which case it will appear on the label of the other room
  • Both rooms are labeled, in which case no action will be taken.
  • The label of one room is smaller, in which case all rooms of a larger label will be assigned a smaller label.

An important note about this function, I use the unary plus operator to create an R-Value int from L-Values int& more details here . A clearer solution would probably be to use static_cast<int> , but for some reason, Visual Studio 2015 is not working, as expected, more information 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; } } 

Using this code, we can:

 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

+6
source

There are several problems with your code that prohibits the correct form of third-party debugging, such as insufficient information on how it works, array overflows undefined variables ( m,n,b ) (multiple access to [411] , and the size is only 411 instead of 412 ), discouraging someone even from trying to try (making you wonder if the code is really useful and worth the time). I was also curious, so here is my simple unoptimized attempt in BDS2006 Turbo C ++ (successor to BCB6 so that this code also works there) for this task.

[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 //--------------------------------------------------------------------------- 

[And one window of the VCL Form app C ++ source without any component]

 //$$---- 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(); } //--------------------------------------------------------------------------- 

My solver's idea for this is simple:

  • ID each grid cell using a separate room number

    remember the number of cells as n

  • combine all the neighboring rooms without a wall between them

    So, go through all the rooms, and if any neighboring cell does not have a wall in the way and has a different ID room, re-index your room number so that the numbers of the stands are the same. Also decrease the room counter n .

  • loop # 2 until re-indexing

example

[Note]

Remember to create the appropriate events in the IDE instead of just copying the code, otherwise it will not work.

+2
source

One easy way is to create an array with the size of the room and initialize it to "0". After that you should iterate over the array. If you find "0", you start the BFS from that point and color the array results in the current number.

BFS Information

BFS should look for a direct neighbor and check if there is a "0" inside this array. If so, the BFS should check to see if there is a wall between the two blocks. If there is no wall between them, paint this field with the current color (at the beginning of 1) and call BFS in a new window.

BFS automatically stops when the room is completely painted. then the global color will be increased by 1, and the cycle will continue and will look like the next "0".

After the cycle, the room count is stored inside the global color value.

this algorithm works in O (n)

A small example:

 //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; } 

btw: BFS is being replaced with DFS, but both work.

Output

enter image description here

+1
source

Separated sets along with Union-Find are well suited for related component problems. Instead of a graph, I track various disjoint sets of floor tiles. Each set has its own representative tile, which uniquely identifies the set.

Read more about Union-Find here .

The algorithm is simple:

  • For each tile, process adjacent tiles if there is no common wall. This is done using a simple union() two plates. When we finish, each tile will belong to one of the disjoint sets (each set represents a connected space or room). The attached parent[] code stores a representative element.

    • When done, normalize each element of the representation.
  • Count the number of unique representative elements. This is the number of disjoint sets (and therefore the number of connected spaces or rooms).

Some additional observations:

  • At any time, you only need to process the walls of the North and West. (Why? The proof remains with the reader.)

    This is because for tile a[i][j] its southern and eastern walls can be treated with tiles; a[i+1][j] and a[i][j+1] respectively.

  • Why do we need to normalize each tile representative? The reason is that some sets may contain two or more representative elements. We recursively find the parent to get a unique representative for the entire set. (If you want to verify this, comment out the following line from the attached code:

     // normalise each tile parent[i] = findSet(parent[i]); 
  • The attached code uses a special version of Union-Find with two heuristics:

    • Union rank (as indicated by ranks[] )
    • Compression path

Attached Code: Live Example

+1
source

All Articles