110.字符串迁移

题目描述

字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:

  1. 序列中第一个字符串是 beginStr。
  2. 序列中最后一个字符串是 endStr。
  3. 每次转换只能改变一个字符。
  4. 转换过程中的中间字符串必须是字典 strList 中的字符串,且strList里的每个字符串只用使用一次。

给你两个字符串 beginStr 和 endStr 和一个字典 strList,找到从 beginStr 到 endStr 的最短转换序列中的字符串数目。如果不存在这样的转换序列,返回 0。

输入描述

第一行包含一个整数 N,表示字典 strList 中的字符串数量。 第二行包含两个字符串,用空格隔开,分别代表 beginStr 和 endStr。 后续 N 行,每行一个字符串,代表 strList 中的字符串。

输出描述

输出一个整数,代表从 beginStr 转换到 endStr 需要的最短转换序列中的字符串数量。如果不存在这样的转换序列,则输出 0。

输入示例

6
abc def
efc
dbc
ebc
dec
dfc
yhn

输出示例

4

提示信息

从 startStr 到 endStr,在 strList 中最短的路径为 abc -> dbc -> dec -> def,所以输出结果为 4

数据范围:

2 <= N <= 500

解题思路

import java.util.*;
class Main {
    static int ans = 0;
    
    static Map<String, Set<String>> map = new HashMap<>();
		// 判断时s2是否是s1的下一个
    static boolean canConverted(String s1, String s2) {
        if (s1.length() != s2.length()) {
            return false;
        }

        int diff = 0, n = s1.length();
        for (int i = 0; i < n; i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                diff++;
                if (diff > 1) {
                    return false;
                }
            }
        }
        return diff == 1;
    }

    static void bfs(String beginStr, String endStr, Map<String, List<String>> graph) {
        Queue<String> que = new LinkedList<>();
        // 存储到达 str 的距离
        Map<String, Integer> dist = new HashMap<>();

        que.offer(beginStr);
        dist.put(beginStr, 1); // 路径长度从 1 开始计

        while (!que.isEmpty()) {
            String cur = que.poll();
            int d = dist.get(cur);

            if (cur.equals(endStr)) {
                ans = d;
                return;
            }

            for (String next : graph.get(cur)) {
                // dist 没有才放入
                if (!dist.containsKey(next)) {
                    dist.put(next, d + 1);
                    que.offer(next);
                }
            }
        }
    }
  
  	static Map<String, List<String>> constructGraph(List<String> strList) {
        Map<String, List<String>> graph = new HashMap<>();
      
      	for (String s : strList) {
            graph.put(s, new ArrayList<>());
        }

        for (int i = 0; i < strList.size(); i++) {
            for (int j = i + 1; j < strList.size(); j++) {
                String a = strList.get(i);
                String b = strList.get(j);

                if (canConverted(a, b)) {
                    graph.get(a).add(b);
                    graph.get(b).add(a);
                }
            }
        }
      
        return graph;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        
        String beginStr = in.next();
        String endStr = in.next();

        List<String> strList = new ArrayList<>();
        strList.add(beginStr);
        strList.add(endStr);
        for (int i = 0; i < n; i++) {
            strList.add(in.next());
        }

        in.close();

        Map<String, List<String>> graph = constructGraph(strList);

        bfs(beginStr, endStr, graph);
        System.out.println(ans);
    }
}

105.有向图的完全联通

题目描述

给定一个有向图,包含 N 个节点,节点编号分别为 1,2,…,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

输入描述

第一行包含两个正整数,表示节点数量 N 和边的数量 K。 后续 K 行,每行两个正整数 s 和 t,表示从 s 节点有一条边单向连接到 t 节点。

输出描述

如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

输入示例

4 4
1 2
2 1
1 3
2 4

输出示例

1

提示信息

img

从 1 号节点可以到达任意节点,输出 1。

数据范围

1 <= N <= 100;
1 <= K <= 2000。

解题思路

import java.util.*;
class Main {
    static void dfs(int[][] graph, int from, boolean[] visited){
        if (visited[from]) {
            return;
        }
        visited[from] = true;
        for (int to = 0; to < graph.length; to++) {
            if (graph[from][to] == 1) {
                dfs(graph, to, visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), k = in.nextInt();
        int[][] graph = new int[n][n];
        for (int i = 0; i < k; i++) {
            int s = in.nextInt() - 1, t = in.nextInt() - 1;
            graph[s][t] = 1;
        }
        in.close();

        boolean[] visited = new boolean[n];

        dfs(graph, 0, visited);
        boolean ans = true;
        // 查看所有节点是否都被访问
        for (boolean v : visited) {
            ans &= v;
        }
        System.out.println(ans ? 1 : -1);
    }
}

106.海岸线计算

题目描述

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。

你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算海岸线,即:岛屿的周长。岛屿内部没有水域。

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

输出一个整数,表示岛屿的周长。

输入示例

5 5
0 0 0 0 0
0 1 0 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0

输出示例

14

提示信息

img

岛屿的周长为 14。

数据范围:

1 <= M, N <= 50。

解题思路

要求海岸线的周长,即求每个岛屿的 上下左右 有几个是临海的,临海则为海岸线。

import java.util.*;
class Main {
    static int dirs[][] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    static int perimeter = 0;

    static void dfs(int[][] martix, int x, int y) {
        for (int[] dir : dirs) {
            int nextX = x + dir[0];
            int nextY = y + dir[1];
            // 下一次遍历的是边界或者是 0 则令周长加 1
            if (nextX < 0 || nextX >= martix.length
            || nextY < 0 || nextY >= martix[0].length
            || martix[nextX][nextY] == 0) { 
                perimeter++;            
            }
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), m = in.nextInt();
        int[][] martix = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                martix[i][j] = in.nextInt();
            }
        }
        in.close();

        boolean[][] visited = new boolean[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (martix[i][j] == 1) {
                    dfs(martix, i, j);   
                }      
            }
        }

        System.out.println(perimeter);
    }
}