围棋
判断围棋棋盘上一个点是不是被包围了。
变种看到过有几个,不知道还有没有。比如被包围方碰到边界,被包围方必须小于几个空才算被包围。
如果是必须被另一种颜色的棋完全包围,可以bfs从当前的棋扩展找相邻的自己颜色的棋或者空的位置。 如果碰不到4个边界,即为被包围了,反之没有被包围。
如果边界也算上的话,则还是刚才的扩展方法,计数能扩展多少个位置,再按“被包围”的定义做决定。
我来简单介绍一下围棋的规则。(以下以白棋举例)
1,如果一个白棋放在棋盘上且不与其它白棋相连,这个棋子上下左右紧邻的可以放子的地方被称为这个白棋的“气”。显然,如果这个棋子的上下左右紧邻的点被黑棋占领,它的气的数量会减小。如果这个白棋本身在边或者角上,气先天比在中央的(上下左右有四个相邻的“气”)白棋要小一些(边是3, 角是2)。
2,如果是一块白棋,在围棋规则中,会把这一块白棋作为一个整体。这时则要考虑的是这一整块白棋的气。这时这一块白棋的气的计算方法为:这块棋子所有的白棋的气的总和(显然,中间的白棋为0气,只有外边的会贡献)。
“气”在围棋中是一个很重要的概念。如果一块/个棋子的气为0,则代表这个棋子已经“死”了,需要从棋盘上取出来。
判断围棋棋盘上一个点是不是被包围了。
000000000000000111111111111110011111111111111100111111111111110 00111111000000 001000 0 00 00000 0111110 011 0 110 011 000 10 0111 0 10 011 00 10 01111110000 00000000000000变种看到过有几个,不知道还有没有。比如被包围方碰到边界,被包围方必须小于几个空才算被包围。
规则是按围棋规则来的,需要干两件事:1找气 2找敌人(需要validate敌人, 如果敌人外面一圈被包了,敌人是无效的)。跑过了几个testcases,欢迎讨论。
这个题说的非常不清楚,主要取决于“被包围”的定义是什么,尤其是对于我这种没有任何围棋知识的人。4个方向算包围?还是8个方向? class ResultType { boolean foundAir, foundEnemy; public ResultType(boolean foundAir, boolean foundEnemy) { this.foundAir = foundAir; this.foundEnemy = foundEnemy; } } int[] dx = {1, 0, 0, -1}; int[] dy = {0, 1, -1, 0}; // 1 black, 0 while, 2 air public boolean isAlive(int[][] board, int i, int j) { // validate given location, outOfBoard || foundAir if (!inbound(board, i, j) || board[i][j] == 2) return false; int val = board[i][j]; ResultType ans = findEmptyNeighbor(board, i, j, val); return ans.foundAir || !ans.foundEnemy; } private ResultType findEmptyNeighbor(int[][] board, int x, int y, int val) { boolean foundAir = false; boolean foundEnemy = false; // found air if (board[x][y] == 2) return new ResultType(true, foundEnemy); // found enemy if (board[x][y] == 1 - val) { // validate enemy board[x][y] = Integer.MAX_VALUE; boolean enemyFoundEnemy = false; for (int k = 0; k < dx.length; k++) { int xx = x + dx[k]; int yy = y + dy[k]; if (!inbound(board, xx, yy) || board[xx][yy] == Integer.MAX_VALUE) continue; ResultType enemyChild = findEmptyNeighbor(board, xx, yy, 1 - val); foundEnemy |= enemyChild.foundAir; enemyFoundEnemy |= enemyChild.foundEnemy; } // two enemy valid conditions: 1. enemy find Air 2. enemy find no its enemy foundEnemy |= !enemyFoundEnemy; return new ResultType(foundAir, foundEnemy); //return false; } // found its kind board[x][y] = Integer.MAX_VALUE; for (int k = 0; k < dx.length; k++) { int xx = x + dx[k]; int yy = y + dy[k]; if (!inbound(board, xx, yy) || board[xx][yy] == Integer.MAX_VALUE) continue; ResultType child = findEmptyNeighbor(board, xx, yy, val); foundAir |= child.foundAir; foundEnemy |= child.foundEnemy; } return new ResultType(foundAir, foundEnemy); } private boolean inbound(int[][] board, int i, int j) { return i >= 0 && i < board.length && j >= 0 && j < board[0].length; }如果是必须被另一种颜色的棋完全包围,可以bfs从当前的棋扩展找相邻的自己颜色的棋或者空的位置。 如果碰不到4个边界,即为被包围了,反之没有被包围。
如果边界也算上的话,则还是刚才的扩展方法,计数能扩展多少个位置,再按“被包围”的定义做决定。
我来简单介绍一下围棋的规则。(以下以白棋举例)
1,如果一个白棋放在棋盘上且不与其它白棋相连,这个棋子上下左右紧邻的可以放子的地方被称为这个白棋的“气”。显然,如果这个棋子的上下左右紧邻的点被黑棋占领,它的气的数量会减小。如果这个白棋本身在边或者角上,气先天比在中央的(上下左右有四个相邻的“气”)白棋要小一些(边是3, 角是2)。
2,如果是一块白棋,在围棋规则中,会把这一块白棋作为一个整体。这时则要考虑的是这一整块白棋的气。这时这一块白棋的气的计算方法为:这块棋子所有的白棋的气的总和(显然,中间的白棋为0气,只有外边的会贡献)。
“气”在围棋中是一个很重要的概念。如果一块/个棋子的气为0,则代表这个棋子已经“死”了,需要从棋盘上取出来。
在第二个例子里面,当前状态下1和0都没有死,因为他们当前都还有气。
最终到底是谁死谁活需要收气(也就是双方博弈,都按照最优走,最终慢了一气的棋子输)。
因此在围棋中有个概念叫做“眼”,只要有两个眼位就能保证这块棋永远不被吃掉
最终到底是谁死谁活需要收气(也就是双方博弈,都按照最优走,最终慢了一气的棋子输)。
因此在围棋中有个概念叫做“眼”,只要有两个眼位就能保证这块棋永远不被吃掉
互相包围双方都没有眼时还有一种有趣的状态叫做“双活”,是指就算互相均按照最优下,也可能谁也吃不了谁的状态。当然这个和之前说的两眼活棋都和本文没有什么关系。
这个题如果较真,感觉是个无底洞,估计可以几个小时不写代码,光讨论各种情况就时间用完了(什么棋的死活,一层围一层的情况),估计还是得限定情况,只能先把最简单的写出来然后慢慢扩展
如果是按照围棋规则中被“围死”完全没有气的那种被称作“包围”的话,用简单的BFS即可,我这里简单写了一个例子。
vector<int> dx = {0, 0, -1, 1}; vector<int> dy = {-1, 1, 0, 0};public: bool isSurround(vector<vector<int>>& grid, int x, int y) { // 0: none, 1: white chess, 2: black chess int n = grid.size(), m = grid[0].size(); int color = grid[x][y]; vector<vector<int>> visited(n, vector<int>(m, 0)); queue<pair<int, int>> q; q.push({x, y}); visited[x][y] = 1; while (!q.empty()) { auto p = q.front(); q.pop(); // current chess int cx = p.first, cy = p.second; for (int i = 0; i < 4; ++i) { // new chess int nx = cx + dx[i], ny = cy + dy[i]; // if adjacent chess is in board and hasn't been visited before if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) { // if adjacent is empty, return false if (grid[nx][ny] == 0) { return false; } // otherwise push it into queue if (grid[nx][ny] == color) { visited[nx][ny] = 1; q.push({nx, ny}); } } } } return true; }
https://www.1point3acres.com/bbs/thread-207550-1-1.html
第一轮:围棋问题,可以用queue做BFS或用recursion做DFS. 判断一片棋子死活就是判断有没有空格邻居。类似Leetcode "number of islands".. From 1point 3acres bbs
必须要紧紧的包围着,不能有空!!!这是重点
XXX
XOO X
XXX
比如上图O就是活的
第一轮:围棋问题,可以用queue做BFS或用recursion做DFS. 判断一片棋子死活就是判断有没有空格邻居。类似Leetcode "number of islands".. From 1point 3acres bbs
- int val; // 1: black, 2: white, 0: empty
- // dfs subroutine to explore same color tokens
- bool findEmptyNeighbor(vector<vector<int>>& board, int i, int j) {
- // didn't find empty grid if out of board
- if (i < 0 || i > 18 || j < 0 || j > 18
- // or visited same color token or opponent token
- || board[i][j] == INT_MAX || board[i][j] == -val) return false; -baidu 1point3acres
- board[i][j] = INT_MAX; // set as "visited"
- // explore 4 neighboring grids
- return findEmptyNeighbor(board, i-1, j) ||
- findEmptyNeighbor(board, i+1, j) ||
- findEmptyNeighbor(board, i, j-1) ||
- findEmptyNeighbor(board, i, j+1);
- }
- bool isAlive(vector<vector<int>>& board, int i, int j) {
- // validate given location (i, j)
- if (i < 0 || i > 18 || j < 0 || j > 18 || board[i][j] == 0) return false;
- val = board[i][j];
- return findEmptyNeighbor(board, i, j);
- }
必须要紧紧的包围着,不能有空!!!这是重点
XXX
XOO X
XXX
比如上图O就是活的
输入一个围棋棋盘,和一个黑棋子坐标,判断这个棋子是活是死,就是search找有没有和黑棋子相连的空格
Follow up:
比如现在每个棋子都有一个status变量,代表这个地方是死是活,问题是当下一个棋子的时候如何更新棋子的status
围棋规则:一片相连(也可以单个)的同色棋子当没有“气”的时候就算死棋,而“气“指的是和这片棋子相邻的空位。例如一个单个在(0,0)的白棋要死必须是紧相邻的4个位置(-1,0), (1,0), (0,1), (0,-1)都被黑棋占据。如果只是一圈黑棋远远(中间有空隙)地包围了一圈的话不算白棋死。
LZ是担心面试官用这个“非正规”的规则,那就不容易判断了。
那就dfs/bfs找到空位就返回true就行了 btw楼主最后一题思路是什么?乍一看有点像quad tree但三角形和depth又是什么
哦,应该在Line 8 and Line 9之间加上 “if (board[j] == 0) return true;” (空位找到). 感谢指出!
这个围棋的算法其实只是“静态”的检查(无法知道下棋的时间顺序)也就是对当前棋子并不做validation。例如:
XXXXXXXXX
XOOOOOX
XOXOXOX
XOOOOOX
XXXXXXXXX
按照算法会认为连成一片的"O" 都是死棋,但在围棋实际中是不可能走到这一步了,因为"O"有至少2个“内眼”,这在围棋中是永活的好棋,因为“X”不可能同时活着占领多个"O"的“内眼”。
补充内容 (2016-10-26 12:32):
应该是board [ \i ] [ j ] == 0. [\i] 被自动认为是斜体的格式化了。