首页 > 试题广场 >

几个岛

[编程题]几个岛
  • 热度指数:3859 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
给定一个m行n列的二维地图, 初始化每个单元都是水.
操作addLand 把单元格(row,col)变成陆地.
岛屿定义为一系列相连的被水单元包围的陆地单元, 横向或纵向相邻的陆地称为相连(斜对角不算).
在一系列addLand的操作过程中, 给出每次addLand操作后岛屿的个数.
二维地图的每条边界外侧假定都是水.

输入描述:
多组测试数据,请参考例题处理 每组数据k+3行, k表示addLand操作次数 第一行:表示行数m 第二行:表示列数n 第三行:表示addLand操作次数k 第4~k+3行:row col 表示addLand的坐标。注意超过边界的坐标是无效的。


输出描述:
一行,k个整数, 表示每次addLand操作后岛屿的个数, 用空格隔开,结尾无空格
示例1

输入

3
3
4
0 0
0 1
1 2
2 1

输出

1 1 2 3

python解法

经典的岛屿问题,又掏出了我的祖传代码。
不过题目有坑:

  • 要判断行数与列数有没有越界。
  • 还有越界后的处理,发现测试用例中对于越界还是取上一次的结果,这个使用数组记录上一次操作后岛屿的数量即可。
  • 一开始要在res数组里做一个初始化值0,防止一上来就越界,此时岛屿数量为0。

代码如下:

import copy


class Solution:
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        if not grid: return 0
        row, col = len(grid), len(grid[0])
        res = 0
        for i in range(row):
            for j in range(col):
                if grid[i][j] == "1":
                    res += 1
                    self.merge(grid, i, j)

        return res

    def merge(self, grid, i, j):
        row = len(grid)
        col = len(grid[0])

        if i < 0 or i >= row or j < 0 or j >= col or grid[i][j] != "1":  # 退出dfs的条件:1.越界;2.遇到值为0或者已访问的节点(X)
            return

        grid[i][j] = "X"  # 置为"X", 表示该元素已被访问。
        """向四个方向查找"""
        self.merge(grid, i - 1, j)
        self.merge(grid, i + 1, j)
        self.merge(grid, i, j - 1)
        self.merge(grid, i, j + 1)


m, n, k = [int(input()) for _ in range(3)]
land, grid = [["0" for i in range(n)] for j in range(m)], []
solution = Solution()
res = [0]
for i in range(k):
    row, col = map(int, input().split())
    if row < m and col < n:
        land[row][col] = "1"
        grid = copy.deepcopy(land)
        res.append(str(solution.numIslands(grid)))
    else:
        res.append(res[-1])
print(" ".join(res[1:]))
发表于 2019-03-20 22:11:22 回复(0)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// function declaration
int Find(int* p, const int x) {
  return p[x] = p[x] ^ x ? Find(p, p[x]) : x;
}

void Union(int* p, const int u, const int v, int* count) {
  const int pu = Find(p, u);
  const int pv = Find(p, v);
  if (pu == pv) return;
  p[pu] = pv;
  --(*count);
}

void printAnswer(int* ans, int ansSize) {
  int i;
  for (i = 0; i < ansSize; ++i) {
    printf("%d", *(ans + i));
    if (i < ansSize - 1) putchar(32);
  }
  putchar('\n');
}

int main(const int argc, const char** argv) {
  int i, m, n, k, x, y, nx, ny;
  scanf("%d%d%d", &m, &n, &k);
  
  int board[m][n], p[m * n], count = 0;
  memset(board, 0x0000, sizeof board);
  for (i = 0; i < m * n; ++i) *(p + i) = i;
  
  static const int dirs[] = { 0, -1, 0, 1, 0 };
  
  int ans[10000], ansSize = 0;
  while (k--) {
    scanf("%d%d", &y, &x);
    if (x < 0 || x >= n || y < 0 || y >= m || board[y][x]) { // 坐标超出地图边界或已经是一块陆地
      *(ans + ansSize++) = count;
      continue;
    }
    board[y][x] = 1; ++count;
    for (i = 0; i < 4; ++i) {
      nx = x + *(dirs + i), ny = y + *(dirs + i + 1);
      if (nx < 0 || ny < 0 || nx == n || ny == n || !board[ny][nx])
        continue;
      Union(p, y * n + x, ny * n + nx, &count);
    }    
    *(ans + ansSize++) = count;
  }
  
  return printAnswer(ans, ansSize), 0;
}

发表于 2021-07-08 15:55:02 回复(0)
#include <iostream>
#include <unordered_set>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 200;
int m, n, k; // 行数m,列数n
int p[N * N];
int g[N][N];

int get(int i, int j) {
    return i * n + j;
}


int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    cin >> m >> n >> k;
    int dx[] = {1, 0, -1, 0}, dy[] = {0, -1, 0, 1};

    // 并查集
    // 1. 初始化并查集
    for (int i = 0; i < m * n; i ++) p[i] = i;

    // 初始化二维地图

    // 2. 对于每个要变成陆地的点1,看它周围相连的有几个大陆,如果有4个不同的大陆,则大陆数减去3,res -= (t - 1)  t表示不同大陆的数量

    // 3. 注意输入的数据可能有重复的,所以重复了你要怎么办
    // 如果这个坐标已经变过陆地了,那直接返回当前的陆地数就行了

    int cnt = 0; // 表示当前的大陆数量

    vector <vector<int> > seg(k, vector<int>(2));
    for (int i = 0; i < k; i ++) {
        cin >> seg[i][0] >> seg[i][1];
    }

    for (int _ = 0; _ < seg.size(); _ ++) {
        int i = seg[_][0], j = seg[_][1];

        // 如果这个坐标已经变过陆地了,或者这个坐标不合法
        // 那直接返回当前的陆地数就行了
        if (g[i][j] == 1 || (i < 0 || i >= m || j < 0 || j >= n)) { 
            cout << cnt << " ";
            continue;
        }

        g[i][j] = 1; // 当前点变为1
        unordered_set <int> st;

        for (int d = 0; d < 4; d ++) {
            int x = i + dx[d], y = j + dy[d];
            if (x >= 0 && x < m && y >= 0 && y < n) {
                if (g[x][y] == 1) {
                    st.insert(find(get(x, y)));
                    p[find(get(i, j))] = find(get(x,y)); // 合并当前相邻的大陆
                }
            }
        }
        cnt -= (st.size() - 1);
        cout << cnt << " ";
    }



    return 0;
}
发表于 2021-01-28 14:31:12 回复(0)

核心思想只有一句(注释那行):

新的岛屿数目 = 原有岛屿数目 + 1 - 现添加位置的联通桥数目

这里的联通桥,指该点与原集合中存在相邻上下左右点(岛)的数目

#islandNum = islandNum+1-countBridge !!!
row,col,optNum = int(input()),int(input()),int(input())
land,islandNum,island = [],0,''
for i in range(optNum):
    addLand = list(map(int,input().split()))
    if addLand[0]>row-1 or \
        addLand[1]<col-1:
        pass
    else:
        loc = (addLand[0],addLand[1])
        bridgeNum = 0
        if loc not in land:
            bridgeNum += int((loc[0]-1,loc[1]) in land)+ \
                 int((loc[0]+1,loc[1]) in land)+ \
                 int((loc[0],loc[1]-1) in land)+ \
                 int((loc[0],loc[1]+1) in land)            
            land.append(loc)
            islandNum += 1-bridgeNum
        if islandNum==0: islandNum = 1
    island += str(islandNum)+' '
print(island[:-1])
############################## 我是分割线 ##############################
19-09-10: 感谢评论的提醒!原提交的代码直接复制进来没注意格式(这里头的代码排版还需要切回html, emmmmm),下面是提交时的代码:

编辑于 2019-09-10 10:12:30 回复(4)
dfs 求连通性


import java.util.Scanner;
public class Main {
    static boolean[][] graph;
    static int m;
    static int n;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        m = scanner.nextInt();
        n = scanner.nextInt();
        int k = scanner.nextInt();
        graph = new boolean[m][n];
        for (int i = 0; i < k; i++) {
            int row = scanner.nextInt();
            int col = scanner.nextInt();
            if (!isOutOfArray(row, col, m, n)) {
                graph[row][col] = true;
            }
            System.out.print(solve() + " ");
        }
        System.out.println();
    }
    private static int solve() {
        boolean[][] visitd = new boolean[m][n];
        int res = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (graph[i][j] && !visitd[i][j]) {
                    res++;
                    dfs(visitd, i, j);
                }
            }
        }
        return res;
    }
    private static void dfs(boolean[][] visitd, int iIndex, int jIndex) {
        if (isOutOfArray(iIndex, jIndex, m, n)) {
            return;
        }
        if (!graph[iIndex][jIndex]) {
            return;
        }
        if (visitd[iIndex][jIndex]) {
            return;
        }
        visitd[iIndex][jIndex] = true;
        dfs(visitd, iIndex, jIndex + 1);
        dfs(visitd, iIndex + 1, jIndex);
        dfs(visitd, iIndex - 1, jIndex);
        dfs(visitd, iIndex, jIndex - 1);
    }
    private static boolean isOutOfArray(int i, int j, int m, int n) {
        return i < 0 || j < 0 || i >= m || j >= n;
    }
}

发表于 2019-05-30 11:17:56 回复(0)
public class Main/*CountIslands*/ {
  // 4个方向
  final static int[][] dir = {{1, -1, 0, 0}, {0, 0, 1, -1}};
  // 将二维信息行号r、列号c根据总列数cor映射成一维
  static int hashCor(int r, int c, int col) {
    return r*col + c + 1;// 加一是因为要将0当作水,加上1避免和0冲突
  }
  // 并查集模板
  // 如果par[x]为0,则代表该点是水
  static int[] par, rank;
  static int find(int x) {
    return x==par[x] ? x : (par[x] = find(par[x]));
  }
  static void union(int x, int y) {
    if ((x=find(x)) == (y=find(y)))
      return;
    if (rank[x] < rank[y]) {
      par[x] = y;
    } else {
      par[y] = x;
      if (rank[x] == rank[y])
        rank[x]++;
    }
  }
  static boolean same(int x, int y) {
    return find(x) == find(y);
  }
  public static void main(String[] args) {
    java.util.Scanner sc = new java.util.Scanner(System.in);
    int isNum, row, col, i, r, c, opNum, nearR, nearC;
    while (sc.hasNext()) {
      row = sc.nextInt();
      col = sc.nextInt();
      opNum = sc.nextInt();
      isNum = 0;// 岛的个数
      par = new int[row*col+1];//一开始每个点的父节点均为0,代表水
      rank = new int[row*col+1];
      while (opNum-- > 0) {
        r = sc.nextInt();
        c = sc.nextInt();
        // 如果行号r和列号c不越界且该点当前是水
        if (r>=0 && r<row && c>=0 && c<col && find(hashCor(r,c,col))==0){
          isNum++;
          par[hashCor(r, c, col)] = hashCor(r, c, col);
          for (i = 0; i < 4; ++i) {//遍历临近4个点的坐标
            nearR = r+dir[0][i];
            nearC = c+dir[1][i];
            // 如果该点不越界且是一块陆地且和当前的这一点不属于同一个岛
            if (nearR>=0 && nearR<row && nearC>=0 && nearC<col &&
                find(hashCor(nearR, nearC, col)) != 0 &&
                !same(hashCor(nearR, nearC, col), hashCor(r, c, col))) {
              // 那么把它们连成同一个岛
              union(hashCor(nearR, nearC, col), hashCor(r, c, col));
              isNum--;// 因为把两块不同的岛连成同一个岛了,所以岛的数目减一
            }
          }
        }
        System.out.printf("%d%c", isNum, opNum==0 ? '\n' : ' ');
      }
    }
  }
}

发表于 2018-09-01 00:24:21 回复(0)
问题解决思路的核心就一句话:
    numOfIsland=numOfIsland+1-countBridge(row,col)
加入一个岛后岛的数目等于原来岛数目加1减去这个岛带来的桥的数目(该岛与其他岛相连称为有1个桥)


import sys
  
m=int(sys.stdin.readline().strip())
n=int(sys.stdin.readline().strip())
k=int(sys.stdin.readline().strip())
arr=[[0 for i in range(n)]for i in range(m)]

def inArray(r,c):
    if r<0 or r==m or c<0 or c==n:
        return 0
    return arr[r][c]

def countBridge(row,col):
    return inArray(row-1,col)+inArray(row+1,col)+inArray(row,col-1)+inArray(row,col+1)

def addLand(row,col,numOfIsland):
    arr[row][col]=1
    numOfIsland=numOfIsland+1-countBridge(row,col)
    if numOfIsland==0:
        numOfIsland=1
    sys.stdout.write(str(numOfIsland)+' ')
    return numOfIsland


numOfIsland=0
while True:
    line= sys.stdin.readline().strip()
    if len(line)==0:break
    [row,col]=map(int,line.split())
    if row<0 or row>m-1 or col<0 or col >n-1:
        sys.stdout.write(str(numOfIsland)+' ')
        continue
    if arr[row][col]==1:
        sys.stdout.write(str(numOfIsland)+' ')
    else:
        numOfIsland=addLand(row,col,numOfIsland)


发表于 2018-10-09 12:48:50 回复(1)
测试用例不全,有些提交通过的代码有问题。思路:
1.增加岛屿序列号,每新增一个岛屿,序列号加一;
2.新增岛屿数初始化为1,检查四个方向,如果不是水,新增岛屿数减一(即为0)。继续检查剩下的方向,如果不是水,并且序列号不等于第一个岛屿,则通过dfs将该岛屿每个单元的序列号改成和第一个岛屿的序列号相同(即合并两个岛屿),新增岛屿数减一。如果序列号与第一个岛屿相同,即为同一个岛屿,不用处理。
3.如果新增岛屿数为1,则新增岛屿为当前最大序列号,最大序列号加一。否则新增岛屿序列号与四个方向岛屿的序列号相同。
4.岛屿数 = 新增前的岛屿数 + 第2步的新增数(取值 1,0,-1,-2,-3)。
#include<iostream>
using namespace std;
int arr[101][101] = {0};
int m, n, k;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
int K = 1;
void dfs(int x, int y, int flag) //将岛屿序列号改成flag
{
    arr[x][y] = flag;
    for (int i=0; i<4; i++)
    {
        int xx = x + dx[i];
        int yy = y + dy[i];
        if (xx>=0 && xx<m && yy>=0 && yy<n && arr[xx][yy]!=0 && arr[xx][yy]!=flag)
            dfs(xx, yy, flag);
    }
}

int check(int x, int y)
{
    int ret = 1;
    int flag = 0;
    for (int i=0; i<4; i++) //检查四个方向
    {
        int xx = x + dx[i];
        int yy = y + dy[i];
        if (xx>=0 && xx<m && yy>=0 && yy<n && arr[xx][yy]!=0)
        {
            if (flag == 0)
            {
                flag = arr[xx][yy]; //第一个岛屿
                ret--;
            } else {
                if (arr[xx][yy] != flag) //与第一个岛屿不连通
                {
                    dfs(xx, yy, flag); //合并到第一个岛屿
                    ret--;
                }
            }
        }
    }
    arr[x][y] = flag!=0 ? flag : K++; //flag==0即四个方向是水    
    return ret; //返回新增岛屿数 1,0,-1,-2,-3
}

int main()
{
    cin >> m >> n >> k;
    
    int cnt = 0;
    for (int i=0; i<k; i++)
    {
        int x, y;
        cin >> x >> y;

        if (x>=0 && x <m && y>=0 && y<n && arr[x][y]==0)
        {
            
            cnt += check(x, y);
        }
        cout << cnt << " ";
    }
    return 0;
}




编辑于 2020-04-11 00:37:33 回复(2)
#include <bits/stdc++.h>
using namespace std;

int n, m, k, id=1;
int G[101][101]={0}, dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

void DFS(int x, int y, int t){
    G[x][y] = t;
    for(int i=0;i<4;i++){
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if(xx>=0 && xx<m && yy>=0 && yy<n && G[xx][yy]!=0 && G[xx][yy]!=t)
            DFS(xx, yy, t);
    }
}

int F(int x, int y){
    int d=1, t=0;
    for(int i=0;i<4;i++){
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if(xx>=0 && xx<m && yy>=0 && yy<n && G[xx][yy]!=0){
            if(t == 0){     // 连到第一个岛
                t = G[xx][yy];
                d--;
            }else{
                if(G[xx][yy] != t){     // 与第一个岛联通
                    DFS(xx, yy, t);
                    d--;
                }
            }
        }
    }
    if(t == 0)      // 独立的岛
        G[x][y] = id++;
    else
        G[x][y] = t;
    return d;
}

int main(){
    int s = 0, x, y;
    scanf("%d%d%d", &m, &n, &k);
    while(k--){
        scanf("%d%d", &x, &y);
        if(x>=0 && x<m && y>=0 && y<n && G[x][y]==0)
            s += F(x, y);
        printf("%d ", s);
    }
    return 0;
}

发表于 2020-11-06 01:38:18 回复(0)
def addLand(land, landu, landd, landl, landr):
    for j in lands:
        if land in j:
            return
    con = []
    i = 0
    while i < len(lands):
        if (landu in lands[i]) or (landd in lands[i]) or (landl in lands[i]) or (landr in lands[i]):
            con += lands[i]
            lands.pop(i)
        else:
            i += 1
    newland = [land]
    for j in con:
        newland.append(j)
    lands.append(newland)

m = int(input())
n = int(input())
k = int(input())
res = 0
landnum = []
lands = []
for i in range(k):
    row, col = map(int, input().split())
    if row < 0 or col < 0 or row >= m or col >= n:
        landnum.append(str(len(lands)))
    else:
        land = [row, col]
        landu = [row-1, col]
        landd = [row+1, col]
        landl = [row, col-1]
        landr = [row, col+1]
        addLand(land, landu, landd, landl, landr)
        landnum.append(str(len(lands)))
print(' '.join(landnum))
发表于 2023-02-07 16:23:11 回复(0)
import java.util.Scanner;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Iterator;
public class Main{
    //定义全局的方向向量,便于用循环来遍历四个方向
     public static int[][] dir=new int[][]{{0,1},{1,0},{0,-1},{-1,0}};
   
    public static void main(String[] args){
        /**
        *1.前期准备工作
        */
        Scanner in=new Scanner(System.in);
        //获取统计参数:行数/列数/操作数
        int rows=in.nextInt();
        int columns=in.nextInt();
        int actions=in.nextInt();
          
        //构造一个地图的数据结构,
                //record[][][0]: 0为水 1为陆地
                //record[][][1]: 唯一标志,标识这个陆地属于哪个岛屿,=加入岛屿的土地数量-1;
        int[][][] record=new int[rows+2][columns+2][2];
 
        //超范围的容器是为了不用判断边界,把边界周围的水也表示出来
        boolean[][] visited=new boolean[rows+2][columns+2];
        
        //设置一个单增序号nextNum,每次添加一块陆地的时候,都会使得该序号增大,
        //可以作为地块的计数.
        //定义一个
        int count=0;
        int nextNum=0;
        
         /**
        *2.操作循环
        */
        while(actions>0){
            int row=in.nextInt()+1;
            int col=in.nextInt()+1;
            int waters=0;
            //利用其去重性,来判断一块地将要连接起来的岛屿数量,表示为set.size();
            Set<Integer> set=new HashSet<Integer>();
             
          /**
        *2.1 当该坐标已经访问过,或者该坐标不在地图范围,则输出当前的岛屿数量
        *    同时操作循环数量自减,这俩不可省略
        *    为了满足要求"结尾无空格",输出时需要判断
        */          
            if(row>rows||col>columns||row<0||col<0|| record[row][col][0]==1){
                if(actions>0){
                    System.out.print(count+" ");
                }else{
                    System.out.print(count);
                }
                 actions--;
                continue;
            }
          /**
        *2.2 如果没有超出地图范围,并且当前是新加入的陆地,则对周围环境进行检索
        *2.2.1记录新地块周围的水的边界数量,以及岛屿的数量(即record[][][1]的值的种类,同一岛屿该值相同,该值为岛屿唯一识别代码)
        *2.2.2判断周围水的边界数量,如果4面都是水,则岛屿计数+1,否则进入下一步;
        *2.2.3判断周围的
        */ 
            //如果没有超出范围,则设置当前位置为陆地,并给他唯一识别符号,同时识别符自增;
            record[row][col][0]=1;
            record[row][col][1]=nextNum++;
            for(int i=0;i<4;i++){          
                int temprec=record[row +dir[i][0]][col+dir[i][1]][0];
                if(temprec==0){
                    waters++;//如果相邻是水,记录周围有多少的水
                }else{
                    set.add(record[row +dir[i][0]][col+dir[i][1]][1]); //如果周围是土地,记录是属于多少个岛屿,将其代号加入其中
                }
            }
            if(waters==4){
                count++;//如果是4面都是水,则认为新增一个陆地,否则就是相连的
            }else {
                count=count-set.size()+1;//否则就是加入了别的岛屿,岛屿数量更新(可能会合并)
                /*3.
                *更新连接的岛屿的全部标识代号*/
                new Main().dfs(row,col,record,new boolean[rows+2][columns+2],nextNum-1);//然后把这个岛的全部位置都遍历一遍,写为新的值
            }
            /**
            *输出格式控制*/
             if(actions>0){
                    System.out.print(count+" ");
                }else{
                    System.out.print(count);
                }
            actions--;//更新当前剩余操作步数
        }
         
    }
      
    /*
    *作用:当前土地加入到了原来的岛屿,或者连接了多个岛屿,此函数基于深度优先原理用于
    *更新当前土地连接的岛屿的所有土地的唯一标识为最新加入元素的标识,一家人整整齐齐,
    *便于下次判断*/
    public  void dfs(int rows,int columns,int[][][] record,boolean[][] visited,int num){
        if(visited[rows][columns]||record[rows][columns][0]==0){
            return;//访问过,或者说是水,则跳过
        }
        visited[rows][columns]=true;//开始访问,写一个访问标记
        record[rows][columns][1]=num;
        for(int i=0;i<4;i++ ){
            dfs(rows+dir[i][0],columns+dir[i][1],record,visited,num);//开始向四面八方递归
        }
    }
  
}

发表于 2022-06-14 22:39:33 回复(1)
# 给定一个m行n列的二维地图, 初始化每个单元都是水.
# 操作addLand 把单元格(row,col)变成陆地.
# 岛屿定义为一系列相连的被水单元包围的陆地单元, 横向或纵向相邻的陆地称为相连(斜对角不算).
# 在一系列addLand的操作过程中, 给出每次addLand操作后岛屿的个数.
# 二维地图的每条边界外侧假定都是水.

# 输入描述:
# 多组测试数据,请参考例题处理 每组数据k+3行, k表示addLand操作次数 第一行:表示行数m 第二行:表示列数n 第三行:表示addLand操作次数k 第4~k+3行:row col 表示addLand的坐标。注意超过边界的坐标是无效的。

# 输出描述:
# 一行,k个整数, 表示每次addLand操作后岛屿的个数, 用空格隔开,结尾无空格

# 输入例子1:
# 3
# 3
# 4
# 0 0
# 0 1
# 1 2
# 2 1

# 输出例子1:
# 1 1 2 3

def run(m,n,coors):
    '''mxn的岛屿,初始化全是水(0),本函数将给定的k个坐标coors所在位置变成岛屿(1),并计算每次修改后的岛屿数量'''
    grid=[[0 for _ in range(n)] for _ in range(m)]

    k=len(coors)# coors:kx2

    res=[0]# 存储最终答案

    def dfs(grid,visited,i,j):
        visited[i][j]=1
        
        for delta_i,delta_j in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
            new_i,new_j=i+delta_i,j+delta_j
            if 0<=new_i<=m-1 and 0<=new_j<=n-1 and not visited[new_i][new_j] and grid[new_i][new_j]==1:
                dfs(grid,visited,new_i,new_j)


    def get_cur_ans(grid,visited):
        m,n=len(grid),len(grid[0])
        ans=0
        for i in range(m):
            for j in range(n):
                if grid[i][j]==1 and not visited[i][j]:
                    ans+=1
                    dfs(grid,visited,i,j)
        return ans



    for kk in range(k):
        x,y=coors[kk]

        # 判断输入的坐标是否越界
        if x<0&nbs***bsp;x>m-1&nbs***bsp;y<0&nbs***bsp;y>n-1:
            res.append(res[-1])
            continue

        grid[x][y]=1

        visited=[[0 for _ in range(n)] for _ in range(m)]#0:未访问;1:已访问
        ans=get_cur_ans(grid,visited)

        res.append(ans)
    
    return res[1:]

while True:
    try:
        m=int(input())
        n=int(input())
        k=int(input())
        coors=[]
        for _ in range(k):
            x,y=map(int, input().split(' '))
            coors.append([x,y])
        res=run(m,n,coors)
        res=map(str,res)
        res=' '.join(res)
        print(res)
    except:
        break

发表于 2022-06-08 15:45:50 回复(0)
比较好想的思路就是每造一次陆地就走一遍“感染算法”计算一下二维矩阵中岛的数量
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    private static int[] dx = new int[]{1, -1, 0, 0};
    private static int[] dy = new int[]{0, 0, 1, -1};
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int m = Integer.parseInt(br.readLine().trim()), n = Integer.parseInt(br.readLine().trim());
        int k = Integer.parseInt(br.readLine().trim());
        int[][] grid = new int[m][n];
        String[] params;
        for(int i = 0; i < k; i++){
            params = br.readLine().trim().split(" ");
            int x = Integer.parseInt(params[0]);
            int y = Integer.parseInt(params[1]);
            if(x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == 1) {
                // 坐标不能越界且不重复造陆地
                System.out.print(getIslandNums(grid) + " ");
                continue;
            }
            grid[x][y] = 1;
            System.out.print(getIslandNums(grid) + " ");
        }
    }
    
    private static int getIslandNums(int[][] map) {
        int m = map.length, n = map[0].length, count = 0;
        int[][] grid = new int[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++) grid[i][j] = map[i][j];
        }
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 1){
                    count ++;
                    infect(i, j, grid);
                }
            }
        }
        return count++;
    }
    
    private static void infect(int i, int j, int[][] map) {
        if(i < 0 || i >= map.length || j < 0 || j >= map[0].length || map[i][j] == 0) return;
        map[i][j] = 0;
        infect(i + 1, j, map);
        infect(i - 1, j, map);
        infect(i, j + 1, map);
        infect(i, j - 1, map);
    }
}

发表于 2021-11-29 20:47:36 回复(0)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextInt()) {
            int row = in.nextInt();
            int col = in.nextInt();
            int[][] grid = new int[row][col];
            int num = in.nextInt();
            int count = 0;
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < num; i++){
                int r = in.nextInt();
                int c = in.nextInt();
                if(isValid(row,col,r,c)){
                    count = 0;
                    grid[r][c] = 1;
                    int[][] temp = new int[grid.length][grid[0].length];
                    //深拷贝
                    for (int k = 0; k < grid.length; k++) {
                        temp[k] = Arrays.copyOf(grid[k], grid[k].length);
                    }
                    for(int a = 0; a < temp.length; a++){
                        for(int b = 0; b < temp[0].length; b++){
                            if(temp[a][b] == 1){
                                count++;
                                dfs(temp,a,b);
                            }
                        }
                    }
                }
                sb.append(count);
                sb.append(" ");
            }
            System.out.println(sb.toString().trim());
        }
    }
    //将与grid[a][b]相邻的陆地都标记为海洋
    public static void dfs(int[][] grid, int a, int b){
        if(!isValid(grid.length, grid[0].length, a, b) || grid[a][b] == 0) return;
        else{
            grid[a][b] = 0;
            dfs(grid,a - 1,b);
            dfs(grid,a + 1,b);
            dfs(grid,a,b + 1);
            dfs(grid,a,b - 1);
        }
    }

    //判断输入合法性
    public static boolean isValid(int row, int col, int r, int c){
        if(r < 0 || r >= row || c < 0 || c >= col) return false;
        return true;
    }
}
我有个疑问求大佬解答,拷贝一个不影响原值的int[][],如上面的20行,是不是必须深拷贝呀?还有什么简便写法嘛? 
发表于 2021-02-15 20:58:38 回复(0)
用并查集将相连接的陆地合并成一个岛屿。
#include <cstdio>
#include <vector>
using namespace std;

int find(vector<int>& pa, int x)
{
    return x==pa[x]?x:pa[x]=find(pa, pa[x]);
}

int main()
{
    int m, n, k;
    const int dx[4] = {0,0,-1,1}, dy[4] = {-1,1,0,0};
    while (scanf("%d%d%d", &m, &n, &k) != EOF)
    {
        int total = m*n;
        vector<int> isWater(total, true);
        vector<int> pa(total);
        for (int i = 0; i < total; ++i) pa[i] = i;
        int count = 0, x, y;
        int curp, newp;
        while (k--)
        {
            scanf("%d%d", &y, &x);
            curp = y*n+x;
            if (x<0||x>=n||y<0||y>=m||!isWater[curp])
            {
                printf("%d ", count);
                continue;
            }
            count++;
            isWater[curp] = false;
            for (int d = 0; d < 4; ++d)
            {
                int newx = x+dx[d], newy = y+dy[d];
                newp = newy*n+newx;
                if (newx<0||newx>=n||newy<0||newy>=m||isWater[newp]) continue;  // 超过边界
                // 周围有陆地
                int paCur = find(pa, curp), paNew = find(pa, newp);
                if (paCur != paNew)  // 两块陆地原本不在一个岛屿,需要合并
                {
                    count--;
                    pa[paCur] = paNew;
                }
            }
            printf("%d ", count);
        }
    }

    return 0;
}
编辑于 2020-12-25 09:17:14 回复(0)
每次新加一个岛屿,index++,判断关联关系,如果岛屿x与新加岛屿连通,并且岛屿x的标志和新加岛屿不同,那么就把所有标志位与岛屿x相同的岛换成标志为index,search数组就把原来岛屿x的标志位清0,search数组最后剩下的非零元素数量就为当前岛屿数

import java.util.Scanner;

public class Main {
    static int[][] land;
    static int index = 0;
    static int m, n, k;
    static int[] dx = { -1, 1, 0, 0 };
    static int[] dy = { 0, 0, -1, 1 };
    static int[] search;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner scanner = new Scanner(System.in);
        m = scanner.nextInt();
        n = scanner.nextInt();
        k = scanner.nextInt();
        land = new int[m][n];
        search = new int[k];
        int x = 0;
        int y = 0;

        for (int i = 0; i < k; i++) {
            x = scanner.nextInt();
            y = scanner.nextInt();
            if (x >= 0 && x < m && y >= 0 && y < n) {
                if (land[x][y] == 0) {
                    addLand(x, y);
                }
            }
            System.out.print(findLandCount() + " ");
        }
        scanner.close();
    }

    public static void addLand(int x, int y) {
        if (index == 0) {// 如果没添加过岛屿,就添加
            index++;
            land[x][y] = index;// 当前岛屿置为当前index
            search[index - 1] = index;// 每个index对应岛屿是否存在
            return;
        }
        index++;// 岛屿数+1
        land[x][y] = index;
        search[index - 1] = index;
        for (int i = 0; i < 4; i++) {
            int xx = x + dx[i];
            int yy = y + dy[i];
            if (xx >= 0 && yy >= 0 && xx < m && yy < n) {// 如果没越界
                if (land[xx][yy] != 0) {// 如果上下左右有值

                    int nowIndex = land[xx][yy];// 上下左右的值赋给nowIndex
                    if (nowIndex != index) {
                        set(nowIndex, index);// 把所有序号为nowIndex的岛屿换成index
                    }
                }
            }
        }
    }

    public static void set(int index, int newIndex) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (land[i][j] == index) {
                    land[i][j] = newIndex;
                }
            }
        }
        search[index - 1] = 0;// 该岛屿没了
    }

    public static int findLandCount() {
        int count = 0;
        for (int i = 0; i < k; i++) {
            if (search[i] != 0) {
                count++;
            }
        }
        return count;
    }
}

发表于 2020-08-24 16:16:49 回复(0)
python3解法

题目有坑:1. 判断越界;2. 判断是否重复填;
思路:1. 每次填岛,先默认岛屿数量加1,对应位置编号设为count;
           2. 搜索四个方向,统计周围岛屿的祖先(并查集,f函数)个数;
           3. 此时岛屿数 = 上一次岛屿数 + 1 - 祖先数;
           4. 将周围岛屿的祖先的祖先设为count;
代码如下:
def f(c, p):
    if p[c] == c:
        return c
    return f(p[c], p)


while True:
    try:
        m = int(input(''))
        n = int(input(''))
        matrix = [[-1] * n for i in range(m)]
        num = int(input(''))
        lands = {}
        count = 0
        results = [0]
        search_dirs = [(-1, 0), (1, 0), (0, 1), (0, -1)]
        for i in range(num):
            x, y = [int(j) for j in input('').split()]
            if x < 0&nbs***bsp;x >= m&nbs***bsp;y < 0&nbs***bsp;y >= n&nbs***bsp;matrix[x][y] > -1:
                results.append(results[-1])
                continue
            s = set()
            matrix[x][y] = count
            for dx, dy in search_dirs:
                next_x, next_y = x + dx, y + dy
                if -1 < next_x < m and -1 < next_y < n and matrix[next_x][next_y] > -1:
                    s.add(f(matrix[next_x][next_y], lands))
            results.append(results[-1] - len(s) + 1)
            for v in s:
                lands[v] = count
            lands[count] = count
            count += 1
        for i in range(1, num):
            print(results[i], end=' ')
        print(results[-1])
    except:
        break


发表于 2020-08-21 10:26:11 回复(0)
思路是:
1. 判断是否越界
2. 判断每次addLand操作,是否与之前的land存在同行或同列
    2.1 如果同行或同列,判断另外一个坐标是否相邻(差值的绝对值小于1)
3. 否则增加一个岛屿
python代码如下:
n = eval(input())
m = eval(input())
k = eval(input())
row_,col_,count_ = [],[],[]
count = 0


def find_all_index(arr, item):
    return [i for i, a in enumerate(arr) if a == item]

for i in range(k):
    row,col = list(map(int,input().split(' ')))
    if row>n or col>m:
        print("坐标无效请重新输入。")
        i=i-1
    elif row in row_:
        temp_ = find_all_index(row_,row)
        for item in temp_:
            if abs(col_[item]-col)<=1:
                row_.append(row)
                col_.append(col)
                count_.append(count)   
            else:
                row_.append(row)
                col_.append(col)
                count = count + 1
                count_.append(count)     
    elif col in col_:
        temp_ = find_all_index(col_,col)
        for item in temp_:
            if abs(row_[item]-row)<=1:
                row_.append(row)
                col_.append(col)     
                count_.append(count)
            else:
                row_.append(row)
                col_.append(col)
                count = count + 1
                count_.append(count)
    else:
        row_.append(row)
        col_.append(col)
        count = count + 1
        count_.append(count)
for item in count_:
    print(item,end=' ')



0 0 到 0 5 为什么会是相邻的岛屿?

发表于 2019-10-18 11:28:15 回复(2)
用了4邻域+BFS的方法,优点是比较好理解,缺点是每次addland操作后要重新计算新地图的连通数
#include <iostream>
#include <memory.h>
#include <vector>
#include <deque>
using namespace std;

int four_neighbors(vector<vector<int> > graph, int rows, int cols) 
{
	bool* visited = new bool[rows*cols];
	memset(visited, false, rows*cols);

	deque<pair<int, int> > deq;      //广搜的关键数据结构--队列
	int types = 0;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			if (graph[i][j] == 1 && !visited[i*cols + j])
			{
				deq.push_back(make_pair(i, j));
				while (!deq.empty())
				{
					int curx = deq.front().first;
					int cury = deq.front().second;
					deq.pop_front();
					// 四邻域
					if (curx + 1 < rows && !visited[(curx + 1)*cols + cury]
						&& graph[curx + 1][cury] == 1)        //down
					{
						deq.push_back(make_pair(curx + 1, cury));
						visited[(curx + 1)*cols + cury] = true;   //加入队列的同时设置为已访问
					}
					if (curx - 1 >= 0 && !visited[(curx - 1)*cols + cury]
						&& graph[curx - 1][cury] == 1)        //up
					{
						deq.push_back(make_pair(curx - 1, cury));
						visited[(curx - 1)*cols + cury] = true;
					}
					if (cury + 1 < cols && !visited[curx*cols + cury + 1]
						&& graph[curx][cury + 1] == 1)        //right
					{
						deq.push_back(make_pair(curx, cury + 1));
						visited[curx*cols + cury + 1] = true;
					}
					if (cury - 1 >= 0 && !visited[curx*cols + cury - 1]
						&& graph[curx][cury - 1] == 1)        //left
					{
						deq.push_back(make_pair(curx, cury - 1));
						visited[curx*cols + cury - 1] = true;
					}
				}
				types++;
			}
		}
	}
	return types;
}

int main()
{
	int m, n;
	while (cin >> m >> n)
	{
		vector<vector<int> > graph(m, vector<int>(n, 0));     //二维地图初始化
		vector<int> res;
		int k;
		cin >> k;
		for (int idx = 0; idx < k; idx++)
		{
			int curx, cury;
			cin >> curx >> cury;
			if (curx < 0 || curx >= m || cury < 0 || cury >= n
				|| graph[curx][cury] == 1) {       //越界处理
				res.push_back(four_neighbors(graph, m, n));
				continue;
			}
			graph[curx][cury] = 1;
			res.push_back(four_neighbors(graph, m, n));
		}
		for (int idx = 0; idx < res.size() - 1; idx++)
			cout << res[idx] << " ";
		cout << res[res.size() - 1] << endl;
	}
	return 0;
}


发表于 2019-09-04 17:08:28 回复(0)
Python3 Union Find Solution.
这个题就是leetcode上比较经典的岛屿问题,DFS也是可以做的,但是我觉得每一次添加一个岛屿,都要重新扫描一次矩阵,感觉非常麻烦,会出现很多重复搜索的情况,所以通过并查集,每一次只需要搜索相邻岛屿的根就可以了。
row = int(input())
col = int(input())
t = int(input())
matrix = [[0 for _ in range(col)] for _ in range(row)]
directions = [[1, 0], [0, 1], [-1, 0], [0, -1]]
res = 0
parent = [i for i in range(row * col)]
rank = [1 for _ in range(row * col)]
ans = []


def find(x):
    if parent[x] != x:
        parent[x] = find(parent[x])
    return parent[x]


def union(x, y):
    px, py = find(x), find(y)
    if px == py:
        return
    else:
        if rank[px] >= rank[py]:
            parent[py] = px
            rank[px] += rank[py]
            rank[py] += rank[px]
        else:
            parent[px] = py
            rank[px] += rank[py]
            rank[py] += rank[px]


for _ in range(t):
    r, c = list(map(int, input().split()))
    if matrix[r][c] == 1 or r < 0 or r >= len(matrix) or c < 0 or c >= len(matrix[0]):
        ans.append(str(res))
        continue
    matrix[r][c] = 1
    temp = set()
    for d in directions:
        newr, newc = r + d[0], c + d[1]
        if 0 <= newr < len(matrix) and 0 <= newc < len(matrix[0]) and matrix[newr][newc] == 1:
            val = find(col * newr + newc)
            temp.add(val)
            union(col * newr + newc, col * r + c)
    if len(temp) == 0:
        res += 1
    elif len(temp) > 1:
        res -= len(temp) - 1
    ans.append(str(res))
print(' '.join(ans))


发表于 2019-08-27 11:24:09 回复(0)