代码随想录第53天 | 图论二三题
字符串接龙
这题的难点在于要如何想到,结合图去求最短路径的问题
字符串接龙
先看一下题目描述:

题目意思是:把开始的字符串,经过一定次数,变为最后的字符串。
规则是:每次转换字母只能抓换一个,且转换的字符串都必须是——在中间的几个字符串相同的
则求这个转化的最小次数
我们可以把所有字符串连接成一个图,最终求这个图从起点到终点的最短路径
那字符串连接的规则是什么呢??
开始字符可以把每个位置转换为其余的25个字母,若这个转化后的字符串在中间出现过(这个操作是判断集合中是否出现过某元素,使用unordered_set<string>会比较好,因为底层实现是哈希查找)——则它俩可连接……
然后既然是需要搜索去求最短路径(使用广搜更便利),则还需要一个数据结构去存储:节点以及到达这个节点的最短路径,显而易见是一个二维的
则使用unordered_map<key,value>,key是string,value是int存储到达该结点的最短路径
既然使用广搜,则还需要定义队列queue<string> q;
分析完所有基本思路,下面就是代码的具体实现
#include<iostream>
#include<unordered_set>
#include<unordered_map>
#include<string>
#include<queue>
using namespace std;int main(){string beginStr, endStr, t;int n;cin>>n;cin>>beginStr>>endStr;unordered_set<string> s;while(n--){cin>>t;s.insert(t);//记录中间节点,以便于后续查找}unordered_map<string,int> m;queue<string> q;m.insert(pair<string, int>(beginStr, 1));//初始化插入开始节点q.push(beginStr);//先把这个开始节点放进去while(!q.empty()){string it=q.front();q.pop();//开始遍历这个字符串的替换方式for(int i=0;i<it.size();i++){string st=it;for(int j=0;j<26;j++){st[i]='a'+j;if (st==endStr) {cout << m[it]+1<< endl; // 找到了路径 return 0;}if(s.find(st)!=s.end()&&m.find(st)==m.end()){//找到!且未被访问过(广搜不可以搜重复的节点)//则需要添加信息m.insert(pair<string, int>(st,m[it]+1));q.push(st);}}}}cout<<0<<endl;return 0;
}有向图的完全可达性(开始涉及有向图)
有向图的完全可达性

给出一个有向图,则——如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1
具体判断是否到达任何节点的方法是
1. 进行图的搜索,遍历每个节点,把到达的节点用一个数组visited记录true。
2. 最终看是否有节点的visited对应为false,则输出-1,否则输出1
那如何存储这个有向图呢?
1. 分析使用邻接表还是邻接矩阵(判断方法是看点和边 的数量差距,差距大使用邻接表比较好,差距小则可以用邻接矩阵)
邻接表:
vector<vector<int>> graph(n+1)
graph[i]存储从节点 i 出发能直接到达的节点列表
DFS:
#include<iostream>
#include<vector>
using namespace std;void dfs(int node, vector<vector<int>>& graph, vector<bool>& visited) {visited[node] = true;for (int neighbor : graph[node]) {//去遍历node节点可以到达的节点if (!visited[neighbor]) {//若未访问则继续进行dfsdfs(neighbor, graph, visited);}}
}int main() {int N, K;cin >> N >> K;vector<vector<int>> graph(N + 1);//邻接表定义for (int i = 0; i < K; i++) {int s, t;cin >> s >> t;graph[s].push_back(t);//存储进去s可到达t}vector<bool> visited(N + 1, false);//用于判断是否到达过这个节点// 从1号节点开始DFSdfs(1, graph, visited);// 检查是否所有节点都被访问for (int i = 1; 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;int main() {int N, K;cin >> N >> K;// 邻接表,节点编号从1到Nvector<vector<int>> graph(N + 1);// 读入边for (int i = 0; i < K; i++) {int s, t;cin >> s >> t;graph[s].push_back(t);}// 访问标记数组vector<bool> visited(N + 1, false);// BFSqueue<int> q;q.push(1);//push进去就要处理visited[1] = true;//处理为已访问while (!q.empty()) {int node = q.front();q.pop();// 遍历当前节点的所有邻居for (int neighbor : graph[node]) {if (!visited[neighbor]) {visited[neighbor] = true;q.push(neighbor);}}}// 检查是否所有节点都被访问for (int i = 1; i <= N; i++) {if (!visited[i]) {cout << -1 << endl;return 0;}}cout << 1 << endl;return 0;
}岛屿的周长
岛屿的周长
这题首先分析,单独一个地面的周长是多少?——4,若周围出现地面,则周长减去1
则我们只需要遍历每个地面,求出其周围(上下左右四个方向)是否有地面?有则减去1
因而——实际上不需要dfs或者bfs进行操作
#include<iostream>
#include<vector>
using namespace std;int main() {int n, m;cin >> n >> m;vector<vector<int>> grid(n, vector<int>(m));for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {cin >> grid[i][j];}}int perimeter = 0;int dx[] = {0, 0, -1, 1};int dy[] = {1, -1, 0, 0};for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (grid[i][j] == 1) {// 每个陆地格子初始周长贡献为4int count = 4;// 检查四个方向for (int k = 0; k < 4; k++) {int ni = i + dx[k];int nj = j + dy[k];// 如果相邻是陆地,减去1if (ni >= 0 && ni < n && nj >= 0 && nj < m && grid[ni][nj] == 1) {count--;}}perimeter += count;}}}cout << perimeter << endl;return 0;
}