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
cpp
#include <iostream>
#include <vector>
using namespace std;
// 四个方向:右、下、左、上
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> graph(n, vector<int>(m));
// 输入网格
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> graph[i][j];
}
}
int res = 0; // 记录周长
// 遍历每一个格子
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果是陆地
if (graph[i][j] == 1) {
// 检查四个方向
for (int k = 0; k < 4; k++) {
int curx = i + dir[k][0];
int cury = j + dir[k][1];
// 如果越界 或 邻居是海洋,则贡献一条边
if (curx < 0 || curx >= n ||
cury < 0 || cury >= m ||
graph[curx][cury] == 0) {
res++;
}
}
}
}
}
// 输出总周长
cout << res << endl;
return 0;
}
总结
1. 核心思路
遍历所有陆地格子,每个格子向四个方向检查:
- 如果该方向是海洋或越界,就说明这条边属于周长
最终把所有这样的边累加起来,就是总周长。
2. 本质理解
每个陆地最多贡献 4 条边:
- 邻居是陆地 → 不算
- 邻居是海洋 / 越界 → 算 1 条边
3. 实现要点
- 四方向遍历
- 判断越界 + 海洋
- 不需要 DFS / BFS
4. 复杂度
- 时间复杂度:O(n * m)
- 空间复杂度:O(1)(不考虑输入)
110. 字符串迁移
题目描述
字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:
序列中第一个字符串是 beginStr。
序列中最后一个字符串是 endStr。
每次转换只能改变一个字符。
转换过程中的中间字符串必须是字典 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
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
using namespace std;
int main() {
int n;
cin >> n;
string beginStr, endStr, str;
cin >> beginStr >> endStr;
// 存储字典中的所有单词,方便 O(1) 查询
unordered_set<string> ust;
for (int i = 0; i < n; i++) {
cin >> str;
ust.insert(str);
}
queue<string> que;
// key:单词
// value:从 beginStr 到该单词的路径长度
unordered_map<string, int> ump;
// 起始单词入队
que.push(beginStr);
ump[beginStr] = 1;
// BFS 搜索最短转换路径
while (!que.empty()) {
string word = que.front();
que.pop();
int path = ump[word];
// 枚举当前单词的每一位
for (int i = 0; i < word.size(); i++) {
string newWord = word;
// 将当前位替换成 a ~ z
for (int j = 0; j < 26; j++) {
newWord[i] = 'a' + j;
// 如果变换后等于目标单词,直接输出最短路径
if (newWord == endStr) {
cout << path + 1 << endl;
return 0;
}
// 如果新单词在字典中,并且之前没有访问过
else if (ust.find(newWord) != ust.end() &&
!ump.count(newWord)) {
ump[newWord] = path + 1;
que.push(newWord);
}
}
}
}
// 无法转换到目标单词
cout << 0 << endl;
return 0;
}
总结
1. 核心思路
这题是典型的 最短路径问题,使用 BFS 解决。
每个单词可以看作一个节点,
如果两个单词只差一个字符,就可以看作它们之间有一条边。
从 beginStr 开始 BFS,第一次遇到 endStr 时,路径长度就是最短转换长度。
2. 实现方式
对当前单词的每一位进行枚举,尝试将它替换成 a ~ z。
如果替换后的单词在字典中,并且没有访问过,就加入队列继续搜索。
3. 关键点
unordered_set:快速判断单词是否在字典中unordered_map:记录每个单词对应的路径长度,同时也起到 visited 的作用- BFS:保证第一次到达目标单词时路径最短
4. 复杂度分析
设单词数量为 n,单词长度为 L。
- 时间复杂度:
O(n * L * 26) - 空间复杂度:
O(n)
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
cpp
// DFS
#include <iostream>
#include <vector>
using namespace std;
// DFS:从起点出发,标记所有能到达的节点
void dfs(vector<vector<int>>& graph, vector<bool>& visited, int x) {
// 如果已经访问过,直接返回
if (visited[x]) return;
// 标记当前节点
visited[x] = true;
// 遍历所有邻接节点
for (int i : graph[x]) {
dfs(graph, visited, i);
}
}
int main() {
int n, m;
cin >> n >> m;
// 邻接表
vector<vector<int>> graph(n + 1);
vector<bool> visited(n + 1, false);
// 输入边(有向图)
for (int i = 0; i < m; i++) {
int s, t;
cin >> s >> t;
graph[s].push_back(t);
}
int start = 1;
// 从 1 开始 DFS
dfs(graph, visited, start);
// 判断是否所有点都能到达
for (int i = 2; i <= n; i++) {
if (!visited[i]) {
cout << -1 << endl;
return 0;
}
}
cout << 1 << endl;
return 0;
}
// BFS
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// BFS:从起点出发,层序遍历图
void bfs(vector<vector<int>>& graph, vector<bool>& visited, int x) {
queue<int> que;
// 起点入队
que.push(x);
visited[x] = true;
while (!que.empty()) {
int cur = que.front();
que.pop();
// 遍历邻接节点
for (int i : graph[cur]) {
if (!visited[i]) {
que.push(i);
visited[i] = true;
}
}
}
}
int main() {
int n, m;
cin >> n >> m;
// 邻接表
vector<vector<int>> graph(n + 1);
vector<bool> visited(n + 1, false);
// 输入边(有向图)
for (int i = 0; i < m; i++) {
int s, t;
cin >> s >> t;
graph[s].push_back(t);
}
int start = 1;
// 从 1 开始 BFS
bfs(graph, visited, start);
// 判断是否所有点都能到达
for (int i = 2; i <= n; i++) {
if (!visited[i]) {
cout << -1 << endl;
return 0;
}
}
cout << 1 << endl;
return 0;
}
总结
1. 核心思路
从节点 1 出发,判断是否能到达图中所有节点。
本质是:
判断图的连通性(从 1 出发是否能遍历所有点)
2. 实现要点
- 使用邻接表存图
- 从
1开始 DFS / BFS - 用
visited标记访问过的节点 - 最后检查是否所有节点都被访问
3. DFS vs BFS
- DFS:递归实现,代码简洁
- BFS:队列实现,更稳定
两者本质一样,都是图遍历
4. 复杂度
- 时间复杂度:O(n + m)
- 空间复杂度:O(n + m)