目录
题目
Description
小张非常喜欢与朋友们玩成语接龙的游戏,但是作为“文化沙漠”的小张,成语的储备量有些不足。现在他的大脑中存储了
个成语,成语中的四个汉字都用一个1000000以内的正整数来表示。现在小张的同学为了考验他给出了他一个成语做开头和一个成语做结尾,如果小张能通过成语接龙的方式说到结尾的成语,他就能够成功完成游戏。他想知道最少能说几个成语能够成功完成游戏。
Input
第一行一个数
。
接下来
行,每行4个1000000以内的正整数,表示一个成语。
下一行4个1000000以内的正整数,表示开始成语。
下一行4个1000000以内的正整数,表示结束成语。
保证开始成语和结束成语在小张的成语储备之中。
Output
一行一个整数,表示最少说几个成语能够完成游戏。如果无法完成输出-1。
Notes
三个成语分别是(1,2,3,4)(4,5,6,7)(7,8,9,10)
思路
通过广度优先搜索遍历成语,找到从起始成语到目标成语的最少步数。使用队列进行广度优先搜索,并使用邻接表存储成语之间的关系。通过标记节点是否被访问和记录到达每个节点的最少步数,可以确保搜索过程正确进行。最后输出最少步数即可。
具体步骤
- 首先,读入成语的数量,并定义起始成语和目标成语的变量。
- 创建一个存储节点是否被访问的数组和一个存储到达每个节点的最少步数的数组。初始化这两个数组。
- 创建一个队列,用于广度优先搜索。创建一个图的邻接表,用于存储成语之间的关系。
- 通过循环读入成语的起始点和终点,并构建图的邻接表。
- 读入起始成语和目标成语。
- 判断起始成语和目标成语是否相同,如果相同则输出1,表示可以直接接龙成功,然后程序结束。
- 否则,将起始成语的最后一个数字入队,标记为已访问,到达该成语的步数初始化为1。
- 进行广度优先搜索,直到队列为空。
- 在搜索过程中,取出队首成语,遍历当前成语可以接龙到的下一个成语。
- 如果下一个成语未被访问过,则将其入队,标记为已访问,到达下一个成语的步数为当前成语的步数加1。
- 如果遇到与目标成语有相同结束数字的成语,将其标记为未访问,并将到达该成语的步数减1。这是为了避免在接龙过程中跳过真正的目标成语。
- 如果遇到目标成语,则直接结束搜索。
- 输出到达目标成语的最少步数
注意事项
-
在搜索过程中,需要注意节点的访问状态和步数的更新。确保节点被正确标记为已访问,并且到达节点的步数正确更新。
-
在遇到与目标成语有相同结束数字的成语时,需要注意回撤操作。确保将该节点标记为未访问,并将到达该节点的步数减1。
-
需要注意代码中的边界情况处理。例如,起始成语和目标成语相同的情况,或者无法从起始成语接龙到目标成语的情况。
C++完整代码(含详细注释)
记得改改再提交乐学啊,别害人害己T T
#include <iostream>
#include <queue>
#include <map>
#include <vector>
using namespace std;
int main() {
int m;
cin >> m; // 输入成语数量
// 定义起始成语和结束成语的变量
int s1, s2, s3, s4, e1, e2, e3, e4;
// 定义存储节点是否被访问的数组
vector<bool> visited(1000001, false);
// 定义存储到达每个节点的最少步数的数组
vector<int> step(1000001, -1);
// 定义用于广度优先搜索的队列
queue<int> que;
// 定义存储图的邻接表
map<int, vector<int>> graph;
// 输入成语的起始点和终点,构建图的邻接表
for (int i = 0; i < m; i++) {
int start, end;
scanf("%d %*d %*d %d", &start, &end);
graph[start].push_back(end);
}
// 输入起始成语和结束成语
cin >> s1 >> s2 >> s3 >> s4;
cin >> e1 >> e2 >> e3 >> e4;
// 判断起始成语和目标结束是否相同,如果相同则输出1,结束程序
if (s1 == e1 && s2 == e2 && s3 == e3 && s4 == e4)
{
cout << 1 << endl;
return 0;
}
else
{
// 将起始成语的最后一个数字入队,标记为已访问,到达该成语的步数初始化为1
que.push(s4);
visited[s4] = true;
step[s4] = 1;
// 广度优先搜索
while (!que.empty())
{
int cur = que.front(); // 取出队首成语
que.pop();
// 遍历当前成语可以接龙到的下一个成语
for (int next : graph[cur])
{
if (!visited[next])
{
que.push(next); // 将下一个成语入队
visited[next] = true; // 标记为已访问
step[next] = step[cur] + 1; // 到达下一个成语的步数加1
//如果遇到了与结束成语有相同结束数字的成语,要复原
//结尾数字代表的visited和step的下标,以后碰到真结尾,因为访问过,会直接跳过,因此要回撤
if (cur != e1 && next == e4)
{
visited[next] = false;
step[next]--;
}
// 如果遇到了结尾成语则直接结束搜索
if (cur == e1 && next == e4)
break;
}
}
}
// 输出到达目标成语的最少步数
cout << step[e4] << endl;
return 0;
}
cout << -1 << endl;
return 0;
}