给你一个由若干 0 和 1 组成的二维网格 grid ,其中 0 表示水,而 1 表示陆地。岛屿由水平方向或竖直方向上相邻的 1(陆地)连接形成。
如果恰好只有一座岛屿 ,则认为陆地是连通的 ;否则,陆地就是分离的 。一天内,可以将任何单个陆地单元(1)更改为水单元(0)。返回使陆地分离的最少天数。
示例 1: 输入:grid = [[0,1,1,0],[0,1,1,0],[0,0,0,0]] 输出:2 解释:至少需要 2天才能得到分离的陆地。 将陆地 grid[1][1] 和 grid[0][2] 更改为水,得到两个分离的岛屿。
示例 2: 输入:grid = [[1,1]]
输出:2 解释:如果网格中都是水,也认为是分离的 ([[1,1]] -> [[0,0]]),0 岛屿。
示例 3: 输入:grid = [[1,0,1,0]] 输出:0
示例 4: 输入:grid = [
[1,1,0,1,1],
[1,1,1,1,1],
[1,1,0,1,1],
[1,1,0,1,1]] 输出:1
示例 5: 输入:grid = [
[1,1,0,1,1],
[1,1,1,1,1],
[1,1,0,1,1],
[1,1,1,1,1]] 输出:2
花了一段时间想到答案不是0就是1,不是1就是2。判断是否是0挺简单的,找到一个1计算连通的1的个数,并与grid中1的总数进行比较。判断是否是1就比较麻烦了,直接的想法是挨个删一删试试,用和上面相同的办法进行判断:
class Solution {
int countLand(int[][] grid, int row, int col) {
grid[row][col] = 0;
int ans = 1;
if(row < grid.length - 1 && grid[row + 1][col] == 1) {
ans += countLand(grid, row + 1, col);
}
if(col < grid[0].length - 1 && grid[row][col + 1] == 1) {
ans += countLand(grid, row, col + 1);
}
if(row > 0 && grid[row - 1][col] == 1) {
ans += countLand(grid, row - 1, col);
}
if(col > 0 && grid[row][col - 1] == 1) {
ans += countLand(grid, row, col - 1);
}
return ans;
}
boolean isSplit(int[][] grid, int num) {
if(num == 0)
return true;
if(num == 1)
return false;
//找到第一个1 搜索 比较连通的陆地大小是不是陆地总大小
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j) {
if(grid[i][j] == 1) {
int[][] copy = new int[grid.length][];
for (int ii = 0; ii < grid.length; ii++) {
copy[ii] = new int[grid[ii].length];
System.arraycopy(grid[ii], 0, copy[ii], 0, grid[ii].length);
}
return countLand(copy, i, j) != num;
}
}
return false;
}
public int minDays(int[][] grid) {
//还真被吓到了
//答案最大不超过2
int num = 0;
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j) {
if(grid[i][j] == 1)
++num;
}
if(isSplit(grid, num)) {
return 0;
}
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j) {
if(grid[i][j] == 1) {
grid[i][j] = 0;
if(isSplit(grid, num - 1)) {
return 1;
}
grid[i][j] = 1;
}
}
return 2;
}
}
27ms 居然没有超时。
看了看别人的参考,有说用Tarjan法的,搜了一下,这篇感觉写的挺好的:Tarjan法求割点。基本上是dfs并对搜索到的树进行判断。判断自己和子树靠返回边能到的序号和父节点的序号,如果返回边等到达的序号不小于父节点的序号,那去了父节点肯定完蛋。实现了一下:
class Solution {
int countLand(int[][] grid, int row, int col) {
grid[row][col] = 0;
int ans = 1;
if(row < grid.length - 1 && grid[row + 1][col] == 1) {
ans += countLand(grid, row + 1, col);
}
if(col < grid[0].length - 1 && grid[row][col + 1] == 1) {
ans += countLand(grid, row, col + 1);
}
if(row > 0 && grid[row - 1][col] == 1) {
ans += countLand(grid, row - 1, col);
}
if(col > 0 && grid[row][col - 1] == 1) {
ans += countLand(grid, row, col - 1);
}
return ans;
}
boolean isSplit(int[][] grid, int num) {
if(num == 0)
return true;
if(num == 1)
return false;
//找到第一个1 搜索 比较连通的陆地大小是不是陆地总大小
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j) {
int[][] copy = new int[grid.length][];
for (int ii = 0; ii < grid.length; ii++) {
copy[ii] = new int[grid[ii].length];
System.arraycopy(grid[ii], 0, copy[ii], 0, grid[ii].length);
}
if(grid[i][j] == 1 && countLand(copy, i, j) != num) {
return true;
}
}
return false;
}
int curDfn;
int[][] dfn;
int[][] low;
int firstChilds;
boolean cut;
void processPoint(int[][] grid, int curR, int curC, int nextR, int nextC, int from) {
if(cut == true)
return;
if(dfn[nextR][nextC] == 0) {
++curDfn;
dfn[nextR][nextC] = curDfn;
low[nextR][nextC] = curDfn;
dfs(grid, nextR, nextC, from);
low[curR][curC] = Math.min(low[curR][curC], low[nextR][nextC]);
if(dfn[curR][curC] != 1 && low[nextR][nextC] >= dfn[curR][curC])
cut = true;
if(dfn[curR][curC] == 1)
++firstChilds;
} else {
low[curR][curC] = Math.min(low[curR][curC], low[nextR][nextC]);
}
}
void dfs(int[][] grid, int row, int col, int from) {
if(cut == true)
return;
if(row < grid.length - 1 && grid[row + 1][col] == 1 && from != 0) {
processPoint(grid, row, col, row + 1, col, 2);
}
if(col < grid[0].length - 1 && grid[row][col + 1] == 1 && from != 1) {
processPoint(grid, row, col, row, col + 1, 3);
}
if(row > 0 && grid[row - 1][col] == 1 && from != 2) {
processPoint(grid, row, col, row - 1, col, 0);
}
if(col > 0 && grid[row][col - 1] == 1 && from != 3) {
processPoint(grid, row, col, row, col - 1, 1);
}
}
boolean hasCut(int[][] grid) {
cut = false;
dfn = new int[grid.length][grid[0].length];//维护访问次序
low = new int[grid.length][grid[0].length];//维护回溯能达到的最小数值
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j){
if(grid[i][j] == 1) {
dfn[i][j] = 1;
low[i][j] = 1;
curDfn = 1;
dfs(grid, i, j, -1);
return cut || firstChilds > 1;
}
}
return cut;
}
public int minDays(int[][] grid) {
int num = 0;
for(int i = 0; i < grid.length; ++i)
for(int j = 0; j < grid[0].length; ++j) {
if(grid[i][j] == 1)
++num;
}
if(isSplit(grid, num)) {
return 0;
}
if(hasCut(grid)) {
return 1;
}
return 2;
}
}
因为是在方法1基础上改的,所以对于初始情况的判别用的和上次一样的方法。居然是74ms…怎么正经做反而慢了啊