采药的最短路径
【描述】少年李逍遥的婶婶病了,孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。
【输入格式】第1行输入两个非零整数 M 和 N ,两者均不大于20。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义:
'@':少年李逍遥所在的位置;'.':可以安全通行的方格;'#':有怪物的方格;'':仙药所在位置。
【输出格式】求李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。
【输入样例】
8 8
.@##...#
#...#.#
#.#.##...
...#.###.
#.#...#.
...###.#.
...#. ...
.#...###
【输出样例】
10
cpp
#include<iostream>
#include<queue>
using namespace std;
#define MAXN 21
int n,m;
int x1,y1,x2,y2; //起点(x1,y1),终点(x2,y2)
struct node{
int x; //行
int y; //列
int step; //步数
};
queue<node> q;
//地图,'@':少年李逍遥所在的位置;'.':可以安全通行的方格;'#':有怪物的方格;'*':仙药所在位置。
char mp[MAXN][MAXN];
bool visited[MAXN][MAXN];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //右下左上
bool flag=false; //是否找到仙药
void bfs(){
node s={x1,y1,0}; visited[x1][y1] = true; q.push(s);
while(!q.empty()){
node cur = q.front(); q.pop();
if(cur.x==x2 && cur.y==y2){
flag=true; cout << cur.step; break;
}
for(int i = 0;i<4;i++){
int nx = cur.x + dir[i][0];
int ny = cur.y + dir[i][1];
//在地图范围内 && 没有访问过 && 可通行
if((nx>=1 && nx<=n && ny>=1 && ny<=m)&& !visited[nx][ny] && mp[nx][ny]!='#'){
visited[nx][ny] = true;
node next={nx,ny,cur.step+1};
q.push(next);
}
}
}
if(!flag) cout<<-1;
}
int main(){
cin>>n>>m;
char t[MAXN];
for(int i=1;i<=n;i++){
cin >> t;//读入一行
for(int j=0;j<m;j++){
if(t[j]=='@') {//记录李逍遥所在的位置
x1 = i; y1 = j+1;
}
if(t[j]=='*') {//记录仙药所在位置
x2 = i; y2 = j+1;
}
mp[i][j+1] = t[j]; //逐个字符保存
}
}
bfs();
return 0;
}
/*
【输入用例2】
6 5
.*.#.
.#...
..##.
.....
.#...
....@
【输出用例2】
8
【输入用例3】
9 6
.#..#.
.#.*.#
.####.
..#...
..#...
..#...
..#...
#.@.##
.#..#.
【输出用例3】
-1
*/
#古希腊之争
【描述】
伯罗奔尼撒战争是以雅典为首的提洛同盟与以斯巴达为首的伯罗奔尼撒联盟之间的一场战争。这场战争从前431年一直持续到前404年,使得绝大多数周边城邦必须加入其中一方的阵营。战争第一阶段(BC431-BC421),雅典在伯里克利的领导之下,凭借强大的海军,采取陆地上防御在海上进攻的策略。而斯巴达在阿基达摩斯二世的领导之下,率领它令人畏惧的战士进行陆地强攻。两个强邦侧重点不同的军事力量导致了战争第一阶段的僵持局面。
话说,有一天阿基达摩斯二世决定率兵进攻雅典的一个居民点阿提卡,当他们满怀斗志的奔向阿提卡的时候,殊不知他们正走向伯利克里所设下的迷宫陷阱之中。当他们发现时,已为时已晚。
As you know, the Magpie Festival is comging!
为了早日返回斯巴达,阿基达摩斯二世立即让所有的斯巴达勇士去需找迷宫的出口E。现在他们被困在迷宫的S点,迷宫中"."表示空地,可以通过,"#"表示墙,不能通过,每次只能向上下左右四个方向移动,每个勇士每移动一个单位距离需要耗费一个单位时间,所有斯巴达勇士的移动速度和方向相同。现在请你计算一下他们所有人要找到迷宫的出口,最短需要时间之和是多少。
PS:假设迷宫中每个点可以容纳的人数没有限制。
【输入描述】
第一行输入三个数n,m, c,(2=<m<=n<=500,100=<c<=10000)分别代表迷宫的长度和宽度,以及被困迷宫的斯巴达勇士数(不包括阿基达摩斯二世)。
下面m行每行有n个字符用来表示迷宫地图。
详细输入格式见样例。
【输出描述】
输出一个整数,表示找到迷宫出口时,所有勇士消耗的最短时间之和,如不能找到出口输入-1。
【用例输入】
5 5 100
#S...#
#...#
#...E
【用例输出】
500
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
int n, m, c; // n: 迷宫列数,m: 迷宫行数,c: 斯巴达勇士数量
cin >> n >> m >> c;
vector<string> grid(m); // 存储迷宫的二维字符串数组(m行,每行n个字符)
int sx = -1, sy = -1; // 起点S的坐标(初始化为-1表示未找到)
int ex = -1, ey = -1; // 终点E的坐标(初始化为-1表示未找到)
// 读取迷宫数据并定位起点S和终点E的位置
for (int i = 0; i < m; ++i) {
cin >> grid[i]; // 逐行读取迷宫的一行数据
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 'S') { // 找到起点S
sx = i;
sy = j;
} else if (grid[i][j] == 'E') { // 找到终点E
ex = i;
ey = j;
}
}
}
// 检查是否成功找到起点和终点(若未找到直接输出-1)
if (sx == -1 || sy == -1 || ex == -1 || ey == -1) {
cout << -1 << endl;
return 0;
}
// BFS初始化:用于记录每个位置的最短距离(初始化为-1表示未访问)
vector<vector<int>> dist(m, vector<int>(n, -1));
queue<pair<int, int>> q; // 队列存储待处理的坐标(行, 列)
dist[sx][sy] = 0; // 起点S的距离初始化为0(已到达)
q.push({sx, sy}); // 将起点加入队列
// 定义四个移动方向(上、下、左、右)
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// BFS遍历:从起点开始扩散,直到队列为空或找到终点
while (!q.empty()) {
pair<int, int> cur = q.front(); // 取出队列头部的当前坐标
q.pop(); // 队首元素出队
int x = cur.first; // 当前行坐标
int y = cur.second; // 当前列坐标
// 提前终止条件:如果当前坐标是终点,无需继续扩散
if (x == ex && y == ey) {
break;
}
// 遍历四个方向,检查相邻格子是否可通行且未被访问过
for (int i = 0; i < 4; ++i) {
int nx = x + dirs[i][0]; // 计算相邻格子的行坐标(当前行+方向偏移)
int ny = y + dirs[i][1]; // 计算相邻格子的列坐标(当前列+方向偏移)
// 检查相邻格子是否在迷宫范围内(不越界)
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
// 检查相邻格子是否是墙('#')或已被访问过(dist != -1)
if (grid[nx][ny] != '#' && dist[nx][ny] == -1) {
dist[nx][ny] = dist[x][y] + 1; // 更新相邻格子的最短距离(当前距离+1)
q.push({nx, ny}); // 将相邻格子加入队列,继续扩散
}
}
}
}
// 输出结果:若终点不可达输出-1,否则输出总时间(勇士数×最短距离)
if (dist[ex][ey] == -1) {
cout << -1 << endl;
} else {
cout << c * dist[ex][ey] << endl;
}
return 0;
}
/*
【输入用例2】
5 6 668
#####
#...#
#.S.#
#...#
#..##
####E
【输出用例2】
-1
【输入用例3】
4 3 200
####
#S#E
####
【输出用例3】
-1
【输入用例4】
3 3 500
###
#SE
###
【输出用例4】
500
*/
古希腊之争(二)
【描述】
话说,年轻的斯巴达勇士们终于走出迷宫,取得胜利并顺利赶了回来。可是等他们回到斯巴达的时候才发现,雅典人趁他们不在偷袭了城邦,并抓走了他们的臣民。侥幸逃出来的几个人说,他们被关押在一个迷宫的牢房里,并把关押他们的迷宫里的情况告诉了年轻的勇士:迷宫中的"S"点表示迷宫的入口,"T"点表示迷宫中牢房的位置,"."表示空地,可以通过,"#"表示墙,不能直接通过,"K"表示陷阱,一旦进入就必死无疑。每次只能向上下左右四个方向移动,每移动一个单位距离需要耗费一个单位时间,所有斯巴达勇士的移动速度相同。
又是迷宫!!!这次斯巴达的勇士们彻底愤怒了!What's more, today is the Magpie Festival!
由于斯巴达的勇士们无比愤怒,而且他们也想尽可能的在今天就能救出他们的臣民。所以当他们在迷宫中遇到墙的阻碍时,也能破墙进入。不过破墙的过程会花费一个单位的时间。现在请你计算一下他们最短需要多少时间才能找到迷宫的牢房。
PS:假设迷宫中每个点可以容纳的人数没有限制,所有勇士都是一起行动的,他们每次会一起向同一个方向移动。
【输入描述】
测试数据第一行输入二个数n,m(2=<m<=n<=20)分别代表迷宫的行数和列数。
下面n行每行有m个字符用来表示迷宫地图。
【输出描述】
输出一个整数,表示找到迷宫出口的最短时间,如不能找到出口输入-1。
【用例输入】
3 4
S#.#
...K.
KKT.
【用例输出】
8
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <climits>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<string> grid(n);
int sx = -1, sy = -1, tx = -1, ty = -1; // 起点S和终点T的坐标
// 读取迷宫并定位起点和终点
for (int i = 0; i < n; ++i) {
cin >> grid[i];
for (int j = 0; j < m; ++j) {
if (grid[i][j] == 'S') {
sx = i;
sy = j;
} else if (grid[i][j] == 'T') {
tx = i;
ty = j;
}
}
}
// 检查是否找到起点或终点
if (sx == -1 || sy == -1 || tx == -1 || ty == -1) {
cout << -1 << endl;
return 0;
}
// 定义四个移动方向(上、下、左、右)
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 初始化距离数组,INF表示不可达
const int INF = INT_MAX;
vector<vector<int>> dist(n, vector<int>(m, INF));
dist[sx][sy] = 0; // 起点距离为0
// 优先队列(小顶堆),元素为(时间, x坐标, y坐标)
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
pq.emplace(0, sx, sy);
while (!pq.empty()) {
auto [t, x, y] = pq.top();
pq.pop();
// 到达终点,输出最短时间
if (x == tx && y == ty) {
cout << t << endl;
return 0;
}
// 如果当前时间大于已记录的最短时间,跳过该节点(已被更优路径处理过)
if (t > dist[x][y]) {
continue;
}
// 遍历四个方向
for (auto& dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
// 检查是否越界
if (nx < 0 || nx >= n || ny < 0 || ny >= m) {
continue;
}
// 遇到陷阱K,跳过
if (grid[nx][ny] == 'K') {
continue;
}
// 计算移动到(nx, ny)的时间:墙需要+2,其他+1
int nt = t + (grid[nx][ny] == '#' ? 2 : 1);
// 如果新时间更短,更新距离并加入队列
if (nt < dist[nx][ny]) {
dist[nx][ny] = nt;
pq.emplace(nt, nx, ny);
}
}
}
// 无法到达终点
cout << -1 << endl;
return 0;
}
/*
【输入用例2】
5 6
#S..KK
#KK...
#KKKK.
###...
KKTKKK
【输出用例2】
12
【输入用例3】
2 2
SK
KT
【输出用例3】
-1
【输入用例4】
2 3
S#T
...
【输出用例4】
3
*/
奇怪的电梯
【描述】有一天 小蓝 做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼 (1≤i≤N) 上有一个数字 Ki(0≤Ki≤N) 。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。
当然,如果不能满足要求,相应的按钮就会失灵。例如: 3,3,1,2,5 代表了 Ki(K1=3,K2=3,...) ,从 1 楼开始。在 1 楼,按"上"可以到 4 楼,按"下"是不起作用的,因为没有 −2 楼。
那么,从 A 楼到 B 楼至少要按几次按钮呢?
【输入描述】
共二行。
第一行为 3 个用空格隔开的正整数,表示 N,A,B(1≤N≤200, 1≤A,B≤N)。
第二行为 N 个用空格隔开的非负整数,表示 Ki 。
【输出描述】
一行,即最少按键次数,若无法到达,则输出 -1 。
【用例输入】
5 1 5
3 3 1 2 5
【用例输出】
3
cpp
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
int n, a, b; // n: 总层数,a: 起点楼层,b: 目标楼层
cin >> n >> a >> b;
vector<int> k(n); // 存储每层的数字Ki(索引0对应1楼)
// 读取每层的Ki值
for (int i = 0; i < n; ++i) {
cin >> k[i];
}
// 特殊情况:起点和目标楼层相同,直接返回0次按键
if (a == b) {
cout << 0 << endl;
return 0;
}
vector<bool> visited(n + 1, false); // 访问标记数组(楼层1~n)
queue<pair<int, int>> q; // 队列存储(当前楼层, 按键次数)
q.push({a, 0}); // 初始状态:起点楼层,0次按键
visited[a] = true; // 标记起点已访问
// BFS遍历
while (!q.empty()) {
auto [cur, step] = q.front(); // 取出队首元素(当前楼层和按键次数)
q.pop();
// 到达目标楼层,输出当前按键次数
if (cur == b) {
cout << step << endl;
return 0;
}
// 计算按上和按下的目标楼层(注意数组索引与楼层的转换)
int up = cur + k[cur - 1]; // 按上:当前楼层 + Ki(Ki是当前楼层的数组值)
int down = cur - k[cur - 1]; // 按下:当前楼层 - Ki
// 检查上方向是否有效(在1~n范围内且未被访问过)
if (up >= 1 && up <= n && !visited[up]) {
visited[up] = true; // 标记为已访问
q.push({up, step + 1}); // 入队,按键次数+1
}
// 检查下方向是否有效(在1~n范围内且未被访问过)
if (down >= 1 && down <= n && !visited[down]) {
visited[down] = true; // 标记为已访问
q.push({down, step + 1}); // 入队,按键次数+1
}
}
// 队列处理完毕仍未找到目标楼层,说明无法到达
cout << -1 << endl;
return 0;
}
/*
【输入用例2】
10 1 1
5 5 5 5 5 5 5 5 5 5
【输出用例2】
0
【输入用例3】
10 7 7
1 2 3 4 5 6 0 8 9 10
【输出用例3】
0
【输入用例4】
4 1 4
0 0 0 0
【输出用例4】
-1
*/