第1题:逃离迷宫
你在一个地下迷宫中找到了宝藏,但是也触发了迷宫机关,导致迷宫将在T分钟后坍塌,为此你需要在T分钟内逃离迷宫,你想知道你能不能逃离迷宫。迷宫是一个边长为m的正方形,其中"S"表示你所在的位置,“E"表示迷宫出口,”.“是可以随意走动的区域,”#“是不可穿行的墙壁,每次你可以耗费1分钟在区域间移动(上下左右四个方向)。
时间限制:1000
内存限制:65536
输入
输入包含多组数组,第一行是一个整数K(1 <= K <= 10),表示有K组数据。接下来每组数组包含整数m(2<=m<=10)和整数T,m表示正方形迷宫的边长,T表示坍塌时间。其后是一个m*m的字符矩阵,包含字符"S”, “E”, “.“和”#”。
输出
每组数据输出一行,输出“YES"或者"NO",表示是否可以在坍塌之前逃离(也就是说移动次数是否可以不超过T)。
样例输入
2
4 7
S…
###.
.#E.
…#.
3 4
S…
…#
.#E
样例输出
YES
NO
这个问题可以通过搜索剪枝技术来解决,具体算法如下:
-
定义一个二维数组
visited
,用于记录每个位置是否被访问过。 -
定义一个队列
queue
,用于存储待访问的位置。 -
定义一个变量
time
,用于记录已经消耗的时间。 -
找到起始位置"S"的坐标,并将其标记为已访问,并将其入队。
-
使用BFS(广度优先搜索)遍历迷宫,直到队列为空或时间超过T:
-
从队列中取出一个位置。
-
如果该位置是出口"E",则返回"YES"。
-
遍历该位置的上下左右四个方向的相邻位置:
-
如果该相邻位置是合法的且未被访问过:
-
将该相邻位置标记为已访问。
-
将该相邻位置入队。
-
-
-
每次遍历完一层(即消耗了1分钟),将
time
加1。
- 如果时间超过了T,返回"NO";否则返回"YES"。
下面是使用C语言实现的代码:
#include <stdio.h>
#include <stdbool.h>
#define MAX_SIZE 10
// 结构体表示坐标
typedef struct {
int x;
int y;
} Coordinate;
// 判断坐标是否在迷宫内
bool isValid(int x, int y, int N) {
return (x >= 0 && x < N && y >= 0 && y < N);
}
// 判断坐标是否可以通过
bool isPassable(char maze[MAX_SIZE][MAX_SIZE], int x, int y) {
return (maze[x][y] == '.' || maze[x][y] == 'E');
}
// 使用BFS搜索迷宫
bool canEscapeMaze(char maze[MAX_SIZE][MAX_SIZE], int N, int T) {
int visited[MAX_SIZE][MAX_SIZE] = {0};
int time = 0;
Coordinate start;
Coordinate end;
// 找到起始位置和出口位置
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (maze[i][j] == 'S') {
start.x = i;
start.y = j;
} else if (maze[i][j] == 'E') {
end.x = i;
end.y = j;
}
}
}
// 定义四个方向的偏移量
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
visited[start.x][start.y] = 1;
// 定义队列
Coordinate queue[MAX_SIZE * MAX_SIZE];
int front = 0;
int rear = 0;
queue[rear++] = start;
// 使用BFS搜索迷宫
while (front < rear && time <= T) {
Coordinate current = queue[front++];
// 到达出口,返回YES
if (current.x == end.x && current.y == end.y) {
return true;
}
// 遍历四个方向
for (int i = 0; i < 4; i++) {
int newX = current.x + dx[i];
int newY = current.y + dy[i];
if (isValid(newX, newY, N) && isPassable(maze, newX, newY) && !visited[newX][newY]) {
visited[newX][newY] = visited[current.x][current.y] + 1;
Coordinate newCoord;
newCoord.x = newX;
newCoord.y = newY;
queue[rear++] = newCoord;
}
}
// 消耗一分钟
time++;
}
return false;
}
int main() {
int K;
scanf("%d", &K);
while (K--) {
int N, T;
scanf("%d %d", &N, &T);
char maze[MAX_SIZE][MAX_SIZE];
// 读取迷宫
for (int i = 0; i <```c
N; i++) {
scanf("%s", maze[i]);
}
// 判断是否可以逃离迷宫
if (canEscapeMaze(maze, N, T)) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
这个程序使用了搜索剪枝技术来判断是否可以在规定时间内逃离迷宫。它通过BFS遍历迷宫,记录每个位置的访问状态和消耗时间,同时判断是否到达出口。如果在消耗的时间内到达出口,则返回"YES";否则返回"NO"。
第2题:夺宝探险
你无意中发现了装满了宝藏的迷宫,你想要获得尽可能多的宝藏,但是迷宫里的机关阻碍了你的计划。迷宫的地面是M行N列的矩形网格,每格是一块带有机关且放置了1个宝藏的地砖,宝藏一共有K种,用1-K表示其种类,迷宫的入口只有一个,为迷宫的第一行第一列。地砖的机关如下:
(1)每次你只能踏到你与你所在地砖相邻的地砖上(即前后左右4块);
(2)当你踏上某块地砖后,其上的宝藏(假设种类为k)自动归属你,同时所有放置了种类为k的宝藏的地砖碎裂,你无法踏上,你当前所在的地砖在你离开后也会立刻碎裂;
(3)当你无路可走的时候,你会被传送回迷宫出口,无法再进入迷宫。
你想知道你最多能获得多少宝藏。
时间限制:1000
内存限制:65536
输入
输入的第一行是三个用空格隔开的整数,分别是M、N和K(1 <= M,N <= 20, 1 <= K <= 100) 之后是M行,每行包含N个范围为1-K的整数,用空格隔开,表示放置的宝藏种类
输出
只有一行,为一个整数,表示最多能获得的宝藏个数。
样例输入
3 4 5
1 2 3 3
2 1 4 3
1 5 1 2
样例输出
4
根据题目描述,可以使用贪心算法来解决这个问题。贪心算法在每一步选择中都采取局部最优的策略,以期望最终达到全局最优。
具体算法如下:
-
定义一个二维数组
maze
,表示迷宫地砖的宝藏种类。 -
定义一个整数变量
count
,表示已获得的宝藏个数,初始化为0。 -
从迷宫的入口开始,用递归或循环的方式遍历迷宫的每个位置:
-
如果当前位置的宝藏种类不为0(即还未被获得),则将该宝藏归属给你,宝藏个数加1,并将当前位置的宝藏种类置为0。
-
在当前位置的上下左右四个方向中选择一个可行的方向,即该方向的位置在迷宫范围内且宝藏种类不为0。
-
如果存在可行的方向,则沿着该方向继续前进。
-
如果不存在可行的方向,则返回迷宫的入口位置,结束探险。
- 返回最终获得的宝藏个数
count
。
下面是使用C语言实现的代码:
#include <stdio.h>
#include <stdbool.h>
#define MAX_SIZE 20
int M, N, K;
int maze[MAX_SIZE][MAX_SIZE];
// 判断坐标是否在迷宫内
bool isValid(int x, int y) {
return (x >= 0 && x < M && y >= 0 && y < N);
}
// 贪心算法探险
int exploreMaze(int x, int y) {
if (!isValid(x, y) || maze[x][y] == 0) {
return 0;
}
int currentTreasure = maze[x][y];
maze[x][y] = 0;
int count = 1;
if (isValid(x + 1, y) && maze[x + 1][y] == currentTreasure) {
count += exploreMaze(x + 1, y);
}
if (isValid(x - 1, y) && maze[x - 1][y] == currentTreasure) {
count += exploreMaze(x - 1, y);
}
if (isValid(x, y + 1) && maze[x][y + 1] == currentTreasure) {
count += exploreMaze(x, y + 1);
}
if (isValid(x, y - 1) && maze[x][y - 1] == currentTreasure) {
count += exploreMaze(x, y - 1);
}
return count;
}
int main() {
scanf("%d %d %d", &M, &N, &K);
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
scanf("%d", &maze[i][j]);
}
}
int maxTreasures = exploreMaze(0, 0);
printf("%d\n", maxTreasures);
return 0;
}
这个程序使用贪心算法来探险迷宫,并计算最多能获得的宝藏个数。它通过递归的方式遍历迷宫的每个位置,如果当前位置有宝藏并且存在可行的方向,就继续前进并累计宝藏个数。最终返回获得的宝藏个数count
。
注意:这个程序的实现假设迷宫的入口位置为(0, 0),即第一行第一列。如果迷宫的入口位置不是固定的,可以根据实际情况进行调整。
第3题:晶矿的个数
在某个区域发现了一些晶矿,已经探明这些晶矿总共有分为两类,为红晶矿和黑晶矿。现在要统计该区域内红晶矿和黑晶矿的个数。假设可以用二维地图m[][]来描述该区域,若m[i][j]为#表示该地点是非晶矿地点,若m[i][j]为r表示该地点是红晶矿地点,若m[i][j]为b表示该地点是黑晶矿地点。一个晶矿是由相同类型的并且上下左右相通的晶矿点组成。现在给你该区域的地图,求红晶矿和黑晶矿的个数。
时间限制:1000
内存限制:65536
输入
第一行为k,表示有k组测试输入。 每组第一行为n,表示该区域由n*n个地点组成,3 <= n<= 30 接下来n行,每行n个字符,表示该地点的类型。
输出
对每组测试数据输出一行,每行两个数字分别是红晶矿和黑晶矿的个数,一个空格隔开。
样例输入
2
6
r##bb#
###b##
#r##b#
#r##b#
#r####
######
4
####
#rrb
#rr#
##bb
样例输出
2 2
1 2
根据题目描述,我们可以使用搜索剪枝技术来解决这个问题。下面是使用C语言编写的解决方案:
#include <stdio.h>
#define MAX_SIZE 30
int countRed;
int countBlack;
void dfs(char map[][MAX_SIZE], int n, int row, int col, char type) {
if (row < 0 || row >= n || col < 0 || col >= n || map[row][col] != type) {
return;
}
map[row][col] = '#'; // 标记为非晶矿点,避免重复计数
// 统计当前晶矿点
if (type == 'r') {
countRed++;
} else if (type == 'b') {
countBlack++;
}
// 递归搜索上下左右相邻的晶矿点
dfs(map, n, row - 1, col, type);
dfs(map, n, row + 1, col, type);
dfs(map, n, row, col - 1, type);
dfs(map, n, row, col + 1, type);
}
void countCrystals(char map[][MAX_SIZE], int n) {
countRed = 0;
countBlack = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (map[i][j] == 'r' || map[i][j] == 'b') {
dfs(map, n, i, j, map[i][j]);
}
}
}
printf("%d %d\n", countRed, countBlack);
}
int main() {
int k;
scanf("%d", &k);
while (k--) {
int n;
scanf("%d", &n);
char map[MAX_SIZE][MAX_SIZE];
for (int i = 0; i < n; i++) {
scanf("%s", map[i]);
}
countCrystals(map, n);
}
return 0;
}
这个解决方案使用了深度优先搜索(DFS)算法,通过递归地搜索相邻的晶矿点,并将其标记为非晶矿点,避免重复计数。在每次搜索过程中,统计红晶矿和黑晶矿的个数。最后输出结果。该算法的时间复杂度为O(n^2),其中n为地图的大小。
第4题:冠军之路
当训练师眼神对上的那一刻,就会开始对战。
lxz来到了冠军之路的山洞中。山洞的地图是一个N*M的矩形。在地图中,'.‘代表可以行走的地面,’#'代表无法行走的岩石。'I’代表山洞的入口,即lxz现在所在的位置。‘O’表示冠军之路的出口。lxz可以向上下左右四个方向行走。矩形的四周都是山洞的岩石,无法行走。
冠军之路中有一些精英训练师,他们可能面向上、下、左、右四个方向,在地图上用’w’,‘a’,‘s’,'d’表示,其中’w’表示向上。's’表示向下。'a’表示向左。'd’表示向右(这些位置不可行走)。如果lxz出现在精英训练师正对方向的一条线上,且没有被岩石或其他精英训练师阻挡,那么lxz就会与这个精英训练师进行对战。每位训练师只会与lxz对战一次。
为了通过冠军之路,lxz必须击败所有精英训练师。lxz希望找到一条击败所有精英训练师并走到冠军之路出口的最短路径。
时间限制:1000
内存限制:65536
输入
第一行是两个整数,N, M表示地图的大小。 0 < N, M <= 100 接下来是N行,每行M个字符,代表冠军之路的地图。训练师的个数不超过8
输出
一个整数,表示击败所有精英训练师并走到冠军之路出口的最短路径的长度。如果无法击败所有精英训练师或者无法到达出口,输出-1。
样例输入
3 3
Id.
…
Oa#
样例输出
8
根据题目描述,我们可以使用搜索剪枝技术来解决这个问题。下面是使用C语言编写的解决方案:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define MAX_SIZE 100
#define INF 1000000
typedef struct {
int row;
int col;
} Position;
int N, M; // 地图的大小
char map[MAX_SIZE][MAX_SIZE]; // 地图
bool visited[MAX_SIZE][MAX_SIZE]; // 记录是否访问过
Position start; // 入口位置
Position end; // 出口位置
Position trainers[8]; // 训练师位置
int trainerCount; // 训练师个数
int minPath; // 最短路径的长度
int min(int a, int b) {
return a < b ? a : b;
}
bool isValid(int row, int col) {
return row >= 0 && row < N && col >= 0 && col < M && map[row][col] != '#';
}
void dfs(int row, int col, int path) {
if (row == end.row && col == end.col) {
if (path < minPath) {
minPath = path;
}
return;
}
if (path >= minPath) {
return;
}
visited[row][col] = true;
if (map[row][col] != 'I' && map[row][col] != 'O') {
for (int i = 0; i < trainerCount; i++) {
if ((row == trainers[i].row || col == trainers[i].col) && !visited[trainers[i].row][trainers[i].col]) {
return; // 与训练师在同一条线上,但被其他岩石或训练师阻挡
}
}
}
if (isValid(row - 1, col) && !visited[row - 1][col]) {
dfs(row - 1, col, path + 1);
}
if (isValid(row + 1, col) && !visited[row + 1][col]) {
dfs(row + 1, col, path + 1);
}
if (isValid(row, col - 1) && !visited[row][col - 1]) {
dfs(row, col - 1, path + 1);
}
if (isValid(row, col + 1) && !visited[row][col + 1]) {
dfs(row, col + 1, path + 1);
}
visited[row][col] = false;
}
int calculateMinPath() {
memset(visited, false, sizeof(visited));
minPath = INF;
dfs(start.row, start.col, 0);
return minPath != INF ? minPath : -1;
}
int main() {
scanf("%d %d", &N, &M);
for (int i = 0; i < N; i++) {
scanf("%s", map[i]);
for (int j = 0; j < M; j++) {
if (map[i][j] == 'I') {
start.row = i;
start.col = j;
} else if (map[i][j] == 'O') {
end.row = i;
end.col = j;
} else if (map[i][j] >= 'a' && map[i][j] <= 'z') {
trainers[trainerCount].row = i;
trainers[trainerCount].col = j;
trainerCount++;
}
}
}
int result = calculateMinPath();
printf("%d\n", result);
return 0;
}
这个解决方案使用了深度优先搜索(DFS)算法,在搜索的过程中,使用剪枝技术来提前终止不必要的搜索。首先,我们通过DFS找到从入口到出口的所有路径。在搜索过程中,我们记录训练师的位置,当lxz出现在精英训练师正对方向的一条线上,且没有被岩石或其他精英训练师阻挡时,我们返回到上一层递归,避免进一步搜索。最后,我们计算所有路径中的最短路径,并输出结果。