岛屿问题(八)岛屿的周长
题目描述
给定一个由 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
提示信息
岛屿的周长为 14。
数据范围:
1 <= M, N <= 50。
思路:
首先为了防止有的岛屿外没有水包围,我们给原有的矩阵外包一圈水。
之后遍历矩阵(不需要遍历外面那一圈水),如果当前坐标是陆地,那么遍历它上下左右看是否有水,如果有的话,那一条边就是周长的一部分,就需要算在周长里。
代码如下:
java
import java.util.*;
public class Main{
private static int ans=0;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int m=in.nextInt();
int[][] graph=new int[n+2][m+2];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
graph[i][j]=in.nextInt();
}
}
boolean[][] visited=new boolean[n+2][m+2];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(graph[i][j]==1&&visited[i][j]==false){
visited[i][j]=true;
dfs(graph,visited,i,j);
}
}
}
System.out.println(ans);
}
private static int[][] dir={{0,1},{1,0},{-1,0},{0,-1}};
public static void dfs(int[][] graph,boolean[][] visited,int x,int y){
for(int i=0;i<4;i++){
int nextx=x+dir[i][0];
int nexty=y+dir[i][1];
if(nextx<0||nexty<0||nextx>=graph.length||nexty>=graph[0].length)continue;
if(graph[nextx][nexty]==0)ans++;
if(graph[nextx][nexty]==1&&visited[nextx][nexty]==false){
visited[nextx][nexty]=true;
dfs(graph,visited,nextx,nexty);
}
}
}
}
字符串接龙
题目描述
字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:
序列中第一个字符串是 beginStr。
序列中最后一个字符串是 endStr。
每次转换只能改变一个位置的字符(例如 ftr 可以转化 fty ,但 ftr 不能转化 frx)。
转换过程中的中间字符串必须是字典 strList 中的字符串。
beginStr 和 endStr 不在 字典 strList 中
字符串中只有小写的26个字母
给你两个字符串 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
思路:
首先我们要解决两个问题:
1.图中的边是怎么来的?我们用26个英文字母替换当前字符串的每一个字符,看替换后是否在strList中出现过,如果出现过,那么这里就有一条边。
2.起点和终点的最短路径长度:
直接使用广搜法,只要搜到了终点(也就是endString),那么就一定是最短路径。这里如果用深搜法会比较麻烦,因为需要再到达终点的不同路径中选一条最短路径。
需要注意的是我们需要标记节点是否走过,这里使用set集合来检查会更快一点。
代码如下:
java
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int n=in.nextInt();
String begin=in.next();
String end=in.next();
List<String> list=new ArrayList<>();
list.add(begin);
list.add(end);
for(int i=0;i<n;i++){
list.add(in.next());
}
int ans=bfs(begin,end,list);
System.out.println(ans);
}
public static int bfs(String begin,String end,List<String> list){
int len=1;
Set<String> set=new HashSet<>();//相当于visited
Queue<String> q=new LinkedList<>();
q.offer(begin);
q.offer(null);
while(!q.isEmpty()){
String tmp=q.poll();
if(tmp==null){
if(!q.isEmpty()){
len++;
q.offer(null);
}
continue;
}
set.add(tmp);
char[] array=tmp.toCharArray();
for(int i=0;i<array.length;i++){
char old=array[i];
for(char ch='a';ch<='z';ch++){
array[i]=ch;
String newString=new String(array);
if(!set.contains(newString)&&list.contains(newString)){
set.add(newString);
q.offer(newString);
if(newString.equals(end))return len+1;
}
}
array[i]=old;
}
}
return 0;
}
}
有向图的完全联通
题目描述
给定一个有向图,包含 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
提示信息
从 1 号节点可以到达任意节点,输出 1。
数据范围:
1 <= N <= 100;
1 <= K <= 2000。
思路:
深搜法,如果节点1可以到达节点2,继续看节点2能否到达其他节点,如果节点2能到达其他节点,说明节点1也可以到达这些节点,将这些节点都标记为true。最后遍历所有节点是否都标记为true,如果是的话输出1,否则输出-1。
代码如下:
java
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int k=in.nextInt();
int[][] graph=new int[n+1][n+1];
for(int i=0;i<k;i++){
int x=in.nextInt();
int y=in.nextInt();
graph[x][y]=1;
}
boolean[] visited=new boolean[n+1];
visited[1]=true;
dfs(graph,visited,1,n);
for(int i=1;i<=n;i++){
if(visited[i]==false){
System.out.println(-1);
return;
}
}
System.out.println(1);
}
public static void dfs(int[][] graph,boolean[] visited,int x,int n){
for(int j=1;j<=n;j++){
if(graph[x][j]==1&&visited[j]==false){
visited[j]=true;
dfs(graph,visited,j,n);
}
}
}
}
其实深搜广搜的难度并不在广搜深搜本身,而是如何应用深搜广搜去解决问题,要想明白这个逻辑。