周末加餐 使陆地分离的最少天数

给你一个由若干 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…怎么正经做反而慢了啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值