Problem:
Given a 2D board containing 'X'
and 'O'
, capture all regions surrounded by 'X'
.
A region is captured by flipping all 'O'
s into 'X'
s in that surrounded region .
For example,
X X X X X O O X X X O X X O X X
After running your function, the board should be:
X X X X X X X X X X X X X O X X
Solution:
因为学过一段时间的《数字图像处理》,所以一看到这个题目就想起来了数图中的边缘检测。于是就顺着这个给走下来了,把背景(在board边界之外的)全部看成'O',检测出来'X'的边缘,然后找出一个'X'围起来的圈中的一个内点,然后根据这个内点把圈内的点全部变成'X',然后就得到了结果。
于是:
- 在边界上找出来一个'X'
- 根据这个'X',沿着他的8-邻居(就是上、下、左、右、右上、右下、左上、左下相邻的点都是邻居,相对比的有一个4-邻居,就是只有上、下、左、右相邻的点是邻居)走,找出来一个封闭的曲线
- 然后找出一个内点
- 同样根据这个内点的4-邻居,把圈内的点全部变成'X'
- 达到目的
事实上,在第4步找内点的时候,费了很大的劲,因为没法判断一个点是不是内点(从一个点走向无穷远穿过边界的次数),因为这个边界有点奇葩。最后想到了反射,就是在找这个边界的时候,其实是知道他外面一圈的点的,于是就上、下、45度以边界上的点反射,找到一个内点。这个结论到现在还不清楚对不对。最后写出来了程序如下,在程序中的测试例子是可以通过的。
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
void solve(vector<vector<char> > &board)
{
int On=-1;
pair<int,int> cur=get_firstX(board,On);
vector<pair<int,int> > path;
vector<pair<int,int> > Opath;
get_boundary(cur.first,cur.second,On,board,path,Opath);
pair<int,int> inner_point=get_inner_point(board,path,Opath);
vfill(board,path,inner_point);
}
pair<int,int> get_inner_point(vector<vector<char> > &board,vector<pair<int,int> > &path, vector<pair<int,int> > &Opath)
{
int Onum=Opath.size();
int nrows=board.size();
int ncols=board[0].size();
for(int i=0;i<Onum;i++)
{
pair<int,int> tmp;
if (board[Opath[i].second-1][Opath[i].first+1]=='X' &&
board[Opath[i].second][Opath[i].first+1]=='X' &&
board[Opath[i].second+1][Opath[i].first+1]=='X')
{
tmp=make_pair(Opath[i].first+2,Opath[i].second);
}
else if(board[Opath[i].second-1][Opath[i].first-1]=='X' &&
board[Opath[i].second][Opath[i].first-1]=='X' &&
board[Opath[i].second+1][Opath[i].first-1]=='X')
{
tmp=make_pair(Opath[i].first-2,Opath[i].second);
}
else if(board[Opath[i].second-1][Opath[i].first+1]=='X' &&
board[Opath[i].second-1][Opath[i].first]=='X' &&
board[Opath[i].second-1][Opath[i].first-1]=='X')
{
tmp=make_pair(Opath[i].first,Opath[i].second-2);
}
else if(board[Opath[i].second+1][Opath[i].first+1]=='X' &&
board[Opath[i].second+1][Opath[i].first]=='X' &&
board[Opath[i].second+1][Opath[i].first-1]=='X')
{
tmp=make_pair(Opath[i].first,Opath[i].second+2);
}
else if(board[Opath[i].second+1][Opath[i].first]=='X' &&
board[Opath[i].second][Opath[i].first+1]=='X')
{
tmp=make_pair(Opath[i].first+1,Opath[i].second+1);
}
else if(board[Opath[i].second-1][Opath[i].first]=='X' &&
board[Opath[i].second][Opath[i].first+1]=='X')
{
tmp=make_pair(Opath[i].first-1,Opath[i].second-1);
}
else if(board[Opath[i].second+1][Opath[i].first]=='X' &&
board[Opath[i].second][Opath[i].first-1]=='X')
{
tmp=make_pair(Opath[i].first-1,Opath[i].second+1);
}
else if(board[Opath[i].second-1][Opath[i].first]=='X' &&
board[Opath[i].second][Opath[i].first-1]=='X')
{
tmp=make_pair(Opath[i].first-1,Opath[i].second-1);
}
else
continue;
if (tmp.first<ncols-1 && tmp.first>0 && tmp.second<nrows-1 && tmp.second>0 && !on_path(tmp.first,tmp.second,path))
{
return tmp;
}
else
continue;
}
}
pair<int,int> get_firstX(vector<vector<char> > &board,int &On)
{
//find the first X
int nrows=board.size();
int ncols=board[0].size();
for (int i=0;i<nrows;i++)
for(int j=0;j<ncols;j++)
{
if (i==0 || i==nrows-1 || j==0 || j==ncols-1)
if (board[i][j]=='X')
{
if (i==0) On=2;
if (j==0) On=4;
if (i==nrows-1) On=0;
if (j==ncols-1) On=6;
return make_pair(i,j);
}
}
return make_pair(-1,-1);
}
int get_boundary(int seedx,int seedy,int Orin,vector<vector<char> > &board,
vector<pair<int,int> > &path,vector<pair<int,int> > &Opath)
{
int testx=seedx,testy=seedy;
int curx=seedx,cury=seedy;
int On;
do
{
int testn=(Orin+1)%8;
while(testn%8!=Orin)
{
pair<int,int> tmp=get_by_direction(curx,cury,testn);
testx=tmp.first;
testy=tmp.second;
On=((testn/2)*2+6)%8;
int testc=get_color(testx,testy,board);
if (testc==0)
{
testn=(++testn)%8;
Opath.push_back(make_pair(testx,testy));
}
else if(testc==1)
{
path.push_back(make_pair(curx,cury));
curx=testx;
cury=testy;
Orin=On;
break;
}
}
if (testn%8==Orin)
{
//isolated X
board[curx][cury]='O';
}
}while (curx!=seedx || cury!=seedy);
return 0;
}
pair<int,int> get_by_direction(int curx,int cury,int direction)
{
int testx,testy;
switch(direction)
{
case 0:
testx=curx+1;
testy=cury;
break;
case 1:
testx=curx+1;
testy=cury-1;
break;
case 2:
testx=curx;
testy=cury-1;
break;
case 3:
testx=curx-1;
testy=cury-1;
break;
case 4:
testx=curx-1;
testy=cury;
break;
case 5:
testx=curx-1;
testy=cury+1;
break;
case 6:
testx=curx;
testy=cury+1;
break;
case 7:
testx=curx+1;
testy=cury+1;
break;
}
return make_pair(testx,testy);
}
int get_color(int x,int y,vector<vector<char> > &board)
{
//regard the background as 'O'
int maxx=board[0].size();
int maxy=board.size();
if (x>=maxx || x<0)
return 0;
if (y>=maxy || y<0)
return 0;
return char2int(board[y][x]);
}
int char2int(char x)
{
int res=0;
switch(x)
{
case 'X':
res=1;
break;
case 'O':
res=0;
break;
default:
res=0;
}
return res;
}
void vfill(vector<vector<char> > &board,vector<pair<int,int> > &path,pair<int,int> inner_point)
{
vector<pair<int,int> > Opoint;
pair<int,int> cur=inner_point;
Opoint.push_back(inner_point);
do
{
cur.first=Opoint.back().first;
cur.second=Opoint.back().second;
board[cur.second][cur.first]='Y';
Opoint.pop_back();
for(int d=0;d<8;d+=2)
{
pair<int,int> tmp=get_by_direction(cur.first,cur.second,d);
if (!on_path(tmp.first,tmp.second,path) && board[tmp.second][tmp.first]!='Y')
{
Opoint.push_back(tmp);
}
}
} while(Opoint.size()!=0);
int nrows=board.size();
int ncols=board[0].size();
for(int i=0;i<nrows;i++)
{
for(int j=0;j<ncols;j++)
{
if (board[i][j]=='Y')
{
board[i][j]='X';
}
}
}
}
bool on_path(int x,int y, vector<pair<int,int> > &path)
{
int num=path.size();
for(int i=0;i<num;i++)
{
if (path[i].first== x && path[i].second==y)
return true;
}
return false;
}
};
int main()
{
char bd[][4]={{'X','X','X','X'},{'X','O','O','X'},{'X','X','O','X'},{'X','O','X','X'}};
vector<vector<char> > board(4,vector<char>(4));
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
board[i][j]=bd[i][j];
Solution s;
s.solve(board);
return 0;
}
可是在leetcode上测试的时候,发现总是runtime error,感觉很无力,于是就懒得调试了。到网上看了看别人的代码,原来这么简单,主要的点都在我上面的代码上体现了,就是现在边界上找到一个'O',然后把沿着这个'O'的4-邻居,把它以及与它相连的所有的'O'变成另外一个字母(随便怎么选),然后把剩下的'O'都变成'X',然后把前面的'O'再变回来了,然后结束。
网上的代码的遍历邻居不少是使用的递归实现的,我上面(vfill)的相当于是使用了一个栈。下面是重新整理的代码:
class Solution {
public:
void solve(vector<vector<char>> &board) {
if (board.size()==0) return;
if (board[0].size()==0) return;
pair<int,int> cur=get_firstO(board);
while(cur.first!=-1 || cur.second!=-1)
{
flip_openO(board,cur,'Y');
cur=get_firstO(board);
}
flip_from_to(board,'O','X');
flip_from_to(board,'Y','O');
}
pair<int,int> get_firstO(vector<vector<char> > &board)
{
//find the first X
int nrows=board.size();
int ncols=board[0].size();
for (int i=0;i<nrows;i++)
for(int j=0;j<ncols;j++)
{
if (i==0 || i==nrows-1 || j==0 || j==ncols-1)
if (board[i][j]=='O')
{
return make_pair(j,i);
}
}
return make_pair(-1,-1);
}
void flip_openO(vector<vector<char> > &board,pair<int,int> &start,char to)
{
vector<pair<int,int> > Opoint;
Opoint.push_back(start);
pair<int,int> cur=start;
int nrows=board.size();
int ncols=board[0].size();
do
{
cur.first=Opoint.back().first;
cur.second=Opoint.back().second;
board[cur.second][cur.first]=to;
Opoint.pop_back();
for(int d=0;d<8;d+=2)
{
pair<int,int> tmp=get_by_direction(cur.first,cur.second,d);
if (tmp.first>=0 && tmp.first<ncols &&
tmp.second>=0 && tmp.second<nrows)
{
if (board[tmp.second][tmp.first]=='O')
{
Opoint.push_back(tmp);
}
}
}
} while(Opoint.size()!=0);
}
void flip_from_to(vector<vector<char> > &board,char from, char to)
{
int nrows=board.size();
int ncols=board[0].size();
for(int i=0;i<nrows;i++)
{
for(int j=0;j<ncols;j++)
{
if (board[i][j]==from)
board[i][j]=to;
}
}
}
pair<int,int> get_by_direction(int curx,int cury,int direction)
{
int testx,testy;
switch(direction)
{
case 0:
testx=curx+1;
testy=cury;
break;
case 1:
testx=curx+1;
testy=cury-1;
break;
case 2:
testx=curx;
testy=cury-1;
break;
case 3:
testx=curx-1;
testy=cury-1;
break;
case 4:
testx=curx-1;
testy=cury;
break;
case 5:
testx=curx-1;
testy=cury+1;
break;
case 6:
testx=curx;
testy=cury+1;
break;
case 7:
testx=curx+1;
testy=cury+1;
break;
}
return make_pair(testx,testy);
}
};
在leetcode上测试,全部通过。
其实在上面写自己的代码的时候,学长看到了说我怎么写这么多。现在看来果然是掉坑里了。艾,碰到问题还是不要想那么复杂为好~