第1题:最小新整数
给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0。n的位数为m。 现在从m位中删除k位(0<k < m),求生成的新整数最小为多少? 例如: n = 9128456, k = 2, 则生成的新整数最小为12456
时间限制:1000
内存限制:65536
输入
第一行t, 表示有t组数据; 接下来t行,每一行表示一组测试数据,每组测试数据包含两个数字n, k。
输出
t行,每行一个数字,表示从n中删除k位后得到的最小整数。
样例输入
2
9128456 2
1444 3
样例输出
12456
1
下面是一个使用C语言编写的解决方案,根据给定的要求计算生成的新整数的最小值:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 函数用于删除字符串中的指定位置的字符
void removeChar(char* str, int index) {
int len = strlen(str);
for (int i = index; i < len - 1; i++) {
str[i] = str[i + 1];
}
str[len - 1] = '\0';
}
// 函数用于计算从n中删除k位后得到的最小整数
void calculateMinInteger(char* n, int k) {
int len = strlen(n);
int removed = 0; // 记录已删除的位数
while (k > 0 && removed < len) {
int minIndex = 0; // 记录当前最小位的索引
for (int i = 1; i <= k; i++) {
if (n[i] < n[minIndex]) {
minIndex = i;
}
}
removeChar(n, minIndex); // 删除当前最小位
k -= minIndex; // 更新剩余需要删除的位数
removed++; // 更新已删除的位数
}
printf("%s\n", n);
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++) {
char n[20];
int k;
scanf("%s %d", n, &k);
calculateMinInteger(n, k);
}
return 0;
}
这个解决方案首先定义了两个辅助函数:removeChar()
用于删除字符串中的指定位置的字符,calculateMinInteger()
用于计算从n中删除k位后得到的最小整数。
在 main()
函数中,首先读取要处理的数据组数 t
。接下来,使用一个循环读取每组测试数据,并调用 calculateMinInteger()
函数进行计算和输出结果。
这个解决方案使用了一个字符数组来存储输入的数字 n
。在 calculateMinInteger()
函数中,通过循环选择当前最小的位,并删除该位,直到删除的位数 k
为 0 或已删除的位数达到字符串的长度。
第2题:求逆序对数
对于一个长度为N的整数序列A,满足i < j 且 Ai > Aj.的数对(i,j)称为整数序列A的一个逆序
请求出整数序列A的所有逆序对个数
时间限制:500
内存限制:65536
输入
输入包含多组测试数据,每组测试数据有两行 第一行为整数N(1 <= N <= 20000),当输入0时结束 第二行为N个整数,表示长为N的整数序列
输出
每组数据对应一行,输出逆序对的个数
样例输入
5
1 2 3 4 5
5
5 4 3 2 1
1
1
0
样例输出
0
10
0
下面是一个使用C语言编写的解决方案,用于计算整数序列的逆序对个数:
#include <stdio.h>
#include <stdlib.h>
// 归并排序并计算逆序对的个数
long long mergeSort(int arr[], int temp[], int left, int right) {
long long count = 0;
if (left < right) {
int mid = (left + right) / 2;
// 分别对左右两个子数组进行归并排序并计算逆序对的个数
count += mergeSort(arr, temp, left, mid);
count += mergeSort(arr, temp, mid + 1, right);
// 合并两个有序子数组并计算逆序对的个数
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
count += mid - i + 1; // 添加逆序对的个数
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
// 将合并后的结果拷贝回原数组
for (int m = left; m <= right; m++) {
arr[m] = temp[m];
}
}
return count;
}
int main() {
int N;
while (scanf("%d", &N) == 1 && N != 0) {
int arr[N];
int temp[N];
for (int i = 0; i < N; i++) {
scanf("%d", &arr[i]);
}
long long count = mergeSort(arr, temp, 0, N - 1);
printf("%lld\n", count);
}
return 0;
}
这个解决方案使用归并排序的思想来计算逆序对的个数。它首先读取输入的整数序列和序列的长度,并定义一个临时数组用于归并排序的过程。
在 mergeSort()
函数中,使用递归的方式将原始数组划分为较小的子数组,并分别对左右两个子数组进行归并排序。在合并两个有序子数组的过程中,通过比较元素大小来计算逆序对的个数,并将合并结果存储在临时数组中。最后,将临时数组的内容拷贝回原数组。
在 main()
函数中,使用一个循环读取多组测试数据。每组数据包括整数序列的长度和具体的序列内容。通过调用 mergeSort()
函数计算逆序对的个数,并输出结果。
第3题:密室逃脱
小Y喜欢玩密室逃脱,每次游戏开始时,小Y会进入一个密室,她需要按照顺序解开各个隐藏线索才能成功逃脱密室。小Y非常聪明,解开线索对她来说并不难,但是她有一点懒,她希望在通关过程中移动次数最少。请你帮小Y计算她至少要移动多少次才能成功通关。
密室是m行n列的格子矩阵,小Y从左上角(1,1)进入密室,密室中有三种格子:
墙,以数字0标记
路,以数字1标记
隐藏线索处,以数字( > 1)标记, 代表该线索的难度
小Y需要按照难度递增的顺序解开各个线索,逃脱密室。
时间限制:1000
内存限制:65536
输入
第一行是一个整数 T,表示输入包含 T 组数据,分别是不同的游戏中小Y所处的密室。 对于每组数据,第一行包括两个整数:m(1 <= m <= 100)、n(1 <= n <= 100)。 接下来 m 行,每行有n个数字,第 i 行的第 j 个数字表示密室中第 i 行第 j 列的格子的类型。 题目保证进入密室处(1,1)不是墙壁,线索的难度都不相同。
输出
对于每组数据,你需要输出一个整数,表示小Y在这个密室中至少要移动多少次才能成功通关。 如果小Y不可能解开所有线索,输出-1.
样例输入
2
3 3
1 3 2
1 0 4
10 6 5
3 3
1 3 2
0 0 0
10 6 5
样例输出
8
-1
提示
样例解释:由于需要按难度顺序解开线索,在第一组数据中,小Y第一次移动到3时不能解密,在完成2之后需要回到3.最后小Y解开10时,她成功通关。
下面是一个使用C语言编写的解决方案,用于计算小Y至少需要移动多少次才能成功通关:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// 定义坐标结构体
typedef struct {
int x;
int y;
} Coordinate;
// 定义队列结构体
typedef struct {
Coordinate items[MAX_SIZE];
int front;
int rear;
} Queue;
// 初始化队列
void initQueue(Queue* queue) {
queue->front = 0;
queue->rear = 0;
}
// 判断队列是否为空
int isQueueEmpty(Queue* queue) {
return queue->front == queue->rear;
}
// 入队
void enqueue(Queue* queue, Coordinate item) {
queue->items[queue->rear] = item;
queue->rear = (queue->rear + 1) % MAX_SIZE;
}
// 出队
Coordinate dequeue(Queue* queue) {
Coordinate item = queue->items[queue->front];
queue->front = (queue->front + 1) % MAX_SIZE;
return item;
}
// 判断坐标是否有效
int isValidCoordinate(int m, int n, int x, int y) {
return (x >= 0 && x < m && y >= 0 && y < n);
}
// 计算小Y至少需要移动多少次才能成功通关
int minimumMoves(int maze[][MAX_SIZE], int m, int n) {
Coordinate start = {0, 0}; // 起始位置
Coordinate end = {m - 1, n - 1}; // 终点位置
int visited[MAX_SIZE][MAX_SIZE] = {0}; // 记录每个格子是否访问过
int distance[MAX_SIZE][MAX_SIZE]; // 记录起始位置到每个格子的最短距离
Queue queue;
initQueue(&queue);
enqueue(&queue, start);
visited[start.x][start.y] = 1;
distance[start.x][start.y] = 0;
int dx[4] = {-1, 0, 1, 0}; // 上下左右四个方向
int dy[4] = {0, 1, 0, -1};
while (!isQueueEmpty(&queue)) {
Coordinate current = dequeue(&queue);
// 到达终点,返回最短距离
if (current.x == end.x && current.y == end.y) {
return distance[current.x][current.y];
}
// 遍历当前位置的四个邻居
for (int i = 0; i < 4; i++) {
int nextX = current.x + dx[i];
int nextY = current.y + dy[i];
// 如果邻居坐标有效且未访问过,进行处理
if (isValidCoordinate(m, n, nextX, nextY) && !visited[nextX][nextY]) {
visited[nextX][nextY] = 1;
enqueue(&queue, (Coordinate){nextX, nextY});
distance[nextX][nextY] = distance[current.x][current.y] + 1;
}
}
}
// 无法解开所有线索,返回-1
return -1;
}
int main() {
int T;
scanf("%d", &T);
for (int i = 0; i < T; i++) {
int m, n;
scanf("%d %d", &m, &n);
int maze[MAX_SIZE][MAX_SIZE];
for (int j = 0; j < m; j++) {
for (int k = 0; k < n; k++) {
scanf("%d", &maze[j][k]);
}
}
int minMoves = minimumMoves(maze, m, n);
printf("%d\n", minMoves);
}
return 0;
}
这个解决方案使用了广度优先搜索(BFS)算法来计算小Y至少需要移动多少次才能成功通关。它使用队列来进行广度优先遍历,并记录起始位置到每个格子的最短距离。
在 minimumMoves()
函数中,首先定义起始位置和终点位置,并初始化队列、访问数组和距离数组。然后,使用广度优先搜索算法遍历迷宫,直到到达终点位置或队列为空。在遍历过程中,检查当前位置的四个邻居,如果邻居坐标有效且未访问过,则将邻居坐标入队,并更新访问数组和距离数组。
最后,如果成功到达终点位置,则返回最短距离;否则,返回-1,表示无法解开所有线索。
在 main()
函数中,根据输入的测试数据,依次读取迷宫的大小和格子的类型,并调用 minimumMoves()
函数计算小Y至少需要移动的次数,并输出结果。
第4题:红与黑
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
时间限制:1000
内存限制:65536
输入
包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下 1)‘.’:黑色的瓷砖; 2)‘#’:白色的瓷砖; 3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。 当在一行中读入的是两个零时,表示输入结束。
输出
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
样例输入
6 9
…#.
…#
…
…
…
…
…
#@…#
.#…#.
0 0
样例输出
45
下面是一个使用C语言编写的解决方案,用于计算你从初始位置出发能够到达的黑色瓷砖数:
#include <stdio.h>
#define MAX_SIZE 20
// 定义坐标结构体
typedef struct {
int x;
int y;
} Coordinate;
// 计算你能够到达的黑色瓷砖数
int countBlackTiles(char tiles[][MAX_SIZE], int W, int H, Coordinate start) {
int count = 0; // 统计黑色瓷砖数
// 定义四个方向的偏移量
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
// 创建一个栈来保存待访问的黑色瓷砖坐标
Coordinate stack[MAX_SIZE * MAX_SIZE];
int top = 0;
// 将初始位置标记为已访问,并将其入栈
int visited[MAX_SIZE][MAX_SIZE] = {0};
visited[start.x][start.y] = 1;
stack[top++] = start;
while (top > 0) {
Coordinate current = stack[--top];
count++;
// 遍历当前位置的四个相邻位置
for (int i = 0; i < 4; i++) {
int nextX = current.x + dx[i];
int nextY = current.y + dy[i];
// 如果相邻位置有效且为黑色瓷砖且未访问过,将其标记为已访问并入栈
if (nextX >= 0 && nextX < H && nextY >= 0 && nextY < W &&
tiles[nextX][nextY] == '.' && !visited[nextX][nextY]) {
visited[nextX][nextY] = 1;
stack[top++] = (Coordinate){nextX, nextY};
}
}
}
return count;
}
int main() {
while (1) {
int W, H;
scanf("%d %d", &W, &H);
if (W == 0 && H == 0) {
break;
}
char tiles[MAX_SIZE][MAX_SIZE];
Coordinate start;
for (int i = 0; i < H; i++) {
scanf("%s", tiles[i]);
for (int j = 0; j < W; j++) {
if (tiles[i][j] == '@') {
start.x = i;
start.y = j;
}
}
}
int blackTiles = countBlackTiles(tiles, W, H, start);
printf("%d\n", blackTiles);
}
return 0;
}
这个解决方案使用了深度优先搜索(DFS)算法来计算你能够到达的黑色瓷砖数。它使用栈来进行深度优先遍历,并使用一个二维数组 visited
来记录每个瓷砖是否已经访问过。
在 countBlackTiles()
函数中,首先定义四个方向的偏移量,并创建一个栈和一个 visited
数组。将初始位置标记为已访问,并将其入栈。
然后,使用深度优先搜索算法遍历瓷砖,直到栈为空。在遍历过程中,从栈中弹出一个瓷砖坐标,并将其计入黑色瓷砖数。然后,遍历该瓷砖的四个相邻位置,如果相邻位置有效且为黑色瓷砖且未访问过,则将其标记为已访问并入栈。
最后,返回计数结果。
在 main()
函数中,首先读取输入的瓷砖布局和初始位置。然后,调用 countBlackTiles()
函数计算你能够到达的黑色瓷砖数,并输出结果。