前情描述:在一个二维棋盘(n*m)棋盘上的每个点只有三种情况,要么是黑子(用'X‘表示)、要么是白子(用'0'表示,注意不是O)、要么是空白(‘.‘表示)
要求当下黑子满足在八个方向是的黑子形成一条直线,这样可以把线中间的白子换成黑子,求最后翻转白子最大个数。
深度遍历,把空白位置置为黑,然后判断是否满足要翻白子为黑子的条件(也就是在8个方向中有与之最近的黑点相连,两黑点之间必然是为黑或者为白,不存在空这种情况
用一个队列存储正在进行【翻转】的黑子位置,若队列不为空,取出队首元素,判断该黑点是否在8个方向上是否有翻转的白子,满足翻转的白子进行翻转,并将该点加入队列,接着判断该点的八个方向是否有可翻转的白子……
可翻转的白子个数最大值极为所求解
class Solution {
public:
// 黑子 x 白子 0 空白 .
//
// (-1, 1) (0, 1) (1, 1)
// (-1, 0) (0, 0) (1, 0)
// (-, -1) (0,-1) (1,-1)
//
const int dirs[8][2] ={
{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}
};
//传入 数组 遍历到的黑子的坐标 黑子的八个方位
//注意这里为什么引用了呢?
bool judge(const vector<string>& chessboard, int x, int y, int dx, int dy){
x += dx;
y += dy;
//遍历棋盘中所有棋子 要在棋盘范围内
//往八个方向发散,一点点向外发散,一旦找到方位的第一个黑子直接返回true
while(0 <= x && x < chessboard.size() && 0 <= y && y < chessboard[0].size()){
if(chessboard[x][y] == 'X'){
//如果对面是黑子
return true;
}else if(chessboard[x][y] =='.'){
return false;
}
//是白子 那么就跳过
x += dx;
y += dy;
}
return false;
}
//注意为什么这里没有引用呢? 每一次遍历都会修改棋盘坐标 每一轮遍历都是不同的棋盘
int dfs(vector<string> chessboard, int px, int py){
int cnt = 0;
queue<pair<int, int>> q;
//queue 在队列中插入元素(px1, py1), (px2, py2),...
q.emplace(px, py);
//需要修改状态 把 . 变成 x
chessboard[px][py] = 'X';
//遍历队列q
while(!q.empty()){
pair<int, int> t = q.front();
q.pop();
//检查该点的八个方向
for(int i = 0; i < 8; ++i){
//八个射线中与第一个黑点组成一个线段
if(judge(chessboard, t.first, t.second, dirs[i][0], dirs[i][1])){
//在方位内找到第一个黑子,记录方位
int x = t.first + dirs[i][0];
int y = t.second + dirs[i][1];
//方位中多有不是黑点的点都要修改为黑点,且由近到远,
//并计数翻转个数(前面已经把空白点涂层黑色了)
while(chessboard[x][y] != 'X'){
q.emplace(x, y);
chessboard[x][y] = 'X';
x += dirs[i][0];
y += dirs[i][1];
++cnt;
}
}
}
}
return cnt;
}
int flipChess(vector<string>& chessboard) {
//统计黑子移动次数
int res = 0;
for(int i = 0; i < chessboard.size(); ++i){
for(int j = 0; j < chessboard[0].size(); ++j){
//如果遍历的元素是.说明为空,加入队列
if(chessboard[i][j] == '.'){
res = max(res, dfs(chessboard, i, j));
}
}
}
return res;
}
};
ps:要多练这种,多角度结合,完全像一个小游戏了。