第1题:生日相同 2.0
在一个有180人的大班级中,存在两个人生日相同的概率非常大,现给出每个学生的名字,出生月日。试找出所有生日相同的学生。
时间限制:1000
内存限制:65536
输入
第一行为整数n,表示有n个学生,n ≤ 180。此后每行包含一个字符串和两个整数,分别表示学生的名字(名字第一个字母大写,其余小写,不含空格,且长度小于20)和出生月(1 ≤ m ≤ 12)日(1 ≤ d ≤ 31)。名字、月、日之间用一个空格分隔
输出
每组生日相同的学生,输出一行,其中前两个数字表示月和日,后面跟着所有在当天出生的学生的名字,数字、名字之间都用一个空格分隔。对所有的输出,要求按日期从前到后的顺序输出。 对生日相同的名字,按名字从短到长按序输出,长度相同的按字典序输出。如没有生日相同的学生,输出”None”
样例输入
6
Avril 3 2
Candy 4 5
Tim 3 2
Sufia 4 5
Lagrange 4 5
Bill 3 2
样例输出
3 2 Tim Bill Avril
4 5 Candy Sufia Lagrange
运行测试
控制台:清理控制台
这个问题可以使用哈希表来解决,其中哈希表的键为日期,值为具有相同生日的学生名字的链表。我们可以遍历所有学生,将具有相同日期的学生名字添加到对应日期的链表中。
下面是一个使用哈希表的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 20
#define MAX_STUDENTS 180
typedef struct Student {
char name[MAX_NAME_LENGTH];
int month;
int day;
struct Student* next;
} Student;
typedef struct {
int month;
int day;
Student* students;
} Birthday;
int hash(int month, int day) {
return (month - 1) * 31 + day;
}
void insertStudent(Student** head, Student* student) {
student->next = *head;
*head = student;
}
void printStudents(Student* students) {
if (students == NULL) {
return;
}
printStudents(students->next);
printf("%s ", students->name);
}
int main() {
int n;
scanf("%d", &n);
Birthday birthdays[366]; // 闰年最多366天
memset(birthdays, 0, sizeof(birthdays));
for (int i = 0; i < n; i++) {
Student* student = (Student*)malloc(sizeof(Student));
scanf("%s %d %d", student->name, &(student->month), &(student->day));
student->next = NULL;
int index = hash(student->month, student->day);
insertStudent(&(birthdays[index].students), student);
birthdays[index].month = student->month;
birthdays[index].day = student->day;
}
int found = 0;
for (int i = 0; i < 366; i++) {
if (birthdays[i].students != NULL) {
if (found) {
printf("\n");
}
printf("%d %d ", birthdays[i].month, birthdays[i].day);
printStudents(birthdays[i].students);
found = 1;
}
}
if (!found) {
printf("None");
}
printf("\n");
// 释放内存
for (int i = 0; i < 366; i++) {
Student* curr = birthdays[i].students;
while (curr != NULL) {
Student* temp = curr;
curr = curr->next;
free(temp);
}
}
return 0;
}
在这段代码中,我们首先定义了一个Student
结构体,用于存储学生的信息,包括名字、月份和日期,并添加了一个指针next
,用于构建链表。然后,我们定义了一个Birthday
结构体,用于存储具有相同生日的学生的链表以及生日的月份和日期。
在hash
函数中,我们使用月份和日期来计算哈希值,以便将学生插入到对应的生日链表中。
insertStudent
函数用于将学生插入到链表的头部。
printStudents
函数用于递归地打印链表中的学生名字,按照从后往前的顺序打印。
在main
函数中,我们首先读取学生的数量,并声明一个大小为366的Birthday
数组来存储生日信息。然后,我们遍历所有学生,将具有相同生日的学生名字插入到对应的生日链表中。同时,我们记录每个生日的月份和日期。
接下来,我们遍历Birthday
数组,如果该生日有学生,就按照要求输出生日信息和学生名字。最后,如果没有找到生日相同的学生,我们输出"None"。
你可以将题目给出的示例输入复制到代码中进行测试。记得在程序结束后释放动态分配的内存。
请注意,本代码中的哈希函数是简单的计算方法,不考虑具体的日期和月份的对应关系。在实际应用中,可以根据实际需求设计更合适的哈希函数。
第2题:机器翻译
VariantF的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M,软件会将新单词存入一个未使用的内存单元;若内存中已存入M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
时间限制:1000
内存限制:262144
输入
第一行为两个正整数M 和N,代表内存容量和文章的长度。 第二行为N 个非负整数,按照文章的顺序,每个数(大小不超过1000000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。 对于50%的数据,1<=N、M<=1000; 对于100%的数据,1<=N、M<=1000000。
输出
一个整数,为软件需要查词典的次数。
样例输入
3 7
1 2 1 5 4 4 1
样例输出
5
提示
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况: 空:内存初始状态为空。 1. 1:查找单词1 并调入内存。 2. 1 2:查找单词2 并调入内存。 3. 1 2:在内存中找到单词1。 4. 1 2 5:查找单词5 并调入内存。 5. 2 5 4:查找单词4 并调入内存替代单词1。 6. 2 5 4:在内存中找到单词4。 7. 5 4 1:查找单词1 并调入内存替代单词2。 共计查了5 次词典。
运行测试
这个问题可以使用哈希表和队列来解决。我们可以使用哈希表来实现内存,将单词作为键,译义作为值,以便快速查找和更新译义。同时,我们可以使用队列来实现内存中单词的先后顺序,保证最早进入内存的单词被清空。
下面是一个使用哈希表和队列的示例代码:
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 1000000
typedef struct {
int word;
int translation;
} Entry;
typedef struct {
Entry entries[MAX_N];
int front;
int rear;
} Queue;
typedef struct {
int capacity;
int size;
Entry* entries;
} HashTable;
void initializeQueue(Queue* queue) {
queue->front = 0;
queue->rear = 0;
}
void enqueue(Queue* queue, Entry entry) {
queue->entries[queue->rear] = entry;
queue->rear = (queue->rear + 1) % MAX_N;
}
Entry dequeue(Queue* queue) {
Entry entry = queue->entries[queue->front];
queue->front = (queue->front + 1) % MAX_N;
return entry;
}
void initializeHashTable(HashTable* hashTable, int capacity) {
hashTable->capacity = capacity;
hashTable->size = 0;
hashTable->entries = (Entry*)malloc(capacity * sizeof(Entry));
}
int findTranslation(HashTable* hashTable, int word) {
for (int i = 0; i < hashTable->size; i++) {
if (hashTable->entries[i].word == word) {
return hashTable->entries[i].translation;
}
}
return -1;
}
void insertTranslation(HashTable* hashTable, int word, int translation) {
if (hashTable->size < hashTable->capacity) {
hashTable->entries[hashTable->size].word = word;
hashTable->entries[hashTable->size].translation = translation;
hashTable->size++;
} else {
for (int i = 0; i < hashTable->size - 1; i++) {
hashTable->entries[i] = hashTable->entries[i + 1];
}
hashTable->entries[hashTable->size - 1].word = word;
hashTable->entries[hashTable->size - 1].translation = translation;
}
}
int main() {
int M, N;
scanf("%d %d", &M, &N);
int* words = (int*)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
scanf("%d", &words[i]);
}
Queue queue;
initializeQueue(&queue);
HashTable hashTable;
initializeHashTable(&hashTable, M);
int dictionaryLookups = 0;
for (int i = 0; i < N; i++) {
int word = words[i];
int translation = findTranslation(&hashTable, word);
if (translation == -1) {
dictionaryLookups++;
translation = word; // 假设将单词本身作为译义
if (hashTable.size < hashTable.capacity) {
insertTranslation(&hashTable, word, translation);
} else {
Entry entry = dequeue(&queue);
insertTranslation(&hashTable, word, translation);
enqueue(&queue, entry);
}
}
enqueue(&queue, (Entry){word, translation});
printf("%d\n", dictionaryLookups);
}
free(words);
free(hashTable.entries);
return 0;
}
在这段代码中,我们首先定义了Entry
结构体,用于存储单词和译义的对应关系。
然后,我们定义了Queue
结构体,用于实现内存中单词的先后顺序。队列使用循环数组来实现,包含一个前端指针front
和一个后端指针rear
。
接着,我们定义了HashTable
结构体,用于实现哈希表。哈希表使用线性探测法解决冲突,包含一个容量capacity
、一个当前大小size
和一个存储Entry
结构体的数组entries
。
initializeQueue
函数用于初始化队列。
enqueue
函数用于将单词存入队列。
dequeue
函数用于从队列中取出最早进入的单词。
initializeHashTable
函数用于初始化哈希表。
findTranslation
函数用于在哈希表中查找单词的译义。
insertTranslation
函数用于将单词和译义插入哈希表。
在main
函数中,我们首先读取输入的内存容量M
和文章长度N
,然后读取文章中的单词,并存储在words
数组中。
接下来,我们初始化队列和哈希表,并定义一个变量dictionaryLookups
用于记录查词典的次数。
然后,我们遍历文章中的每个单词,对于每个单词,我们首先在哈希表中查找译义。如果找到译义,则直接使用;如果未找到译义,则进行查词典的操作:增加dictionaryLookups
计数,将单词和译义插入哈希表,并将最早进入内存的单词从队列中移除。
最后,我们输出dictionaryLookups
的值,表示查词典的次数。
这样,我们就可以通过使用哈希表和队列来实现该机器翻译软件,并计算出需要查词典的次数。
第3题:括号画家
Candela是一名漫画家,她有一个奇特的爱好,就是在纸上画括号。这一天,刚刚起床的Candela画了一排括号序列,其中包含小括号()、中括号[]和大括号{},总长度为N。这排随意绘制的括号序列显得杂乱无章,于是Candela定义了什么样的括号序列是美观的:
(1) 空的括号序列是美观的;
(2) 若括号序列A是美观的,则括号序列(A)、[A]、{A}也是美观的;
(3) 若括号序列A、B都是美观的,则括号序列AB也是美观的;
例如 (){} 是美观的括号序列,而 )({)[}]( 则不是。
现在Candela想知道她画出的括号序列是不是美观的。你能帮帮她吗?
时间限制:1000
内存限制:262144
输入
一个括号序列,长度不超过10000。
输出
如果它是美观的,输出Yes,否则输出No。
样例输入
{}(){}
样例输出
Yes
下面是一个使用栈的示例代码,来判断给定的括号序列是否是美观的:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char bracket;
struct Node* next;
} Node;
typedef struct {
Node* top;
} Stack;
void initializeStack(Stack* stack) {
stack->top = NULL;
}
int isEmpty(Stack* stack) {
return stack->top == NULL;
}
void push(Stack* stack, char bracket) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->bracket = bracket;
newNode->next = stack->top;
stack->top = newNode;
}
char pop(Stack* stack) {
if (isEmpty(stack)) {
printf("Error: Stack is empty.\n");
exit(EXIT_FAILURE);
}
char bracket = stack->top->bracket;
Node* temp = stack->top;
stack->top = stack->top->next;
free(temp);
return bracket;
}
int isMatchingPair(char opening, char closing) {
if (opening == '(' && closing == ')')
return 1;
else if (opening == '[' && closing == ']')
return 1;
else if (opening == '{' && closing == '}')
return 1;
else
return 0;
}
int isBeautiful(char* expression) {
Stack stack;
initializeStack(&stack);
for (int i = 0; expression[i] != '\0'; i++) {
if (expression[i] == '(' || expression[i] == '[' || expression[i] == '{')
push(&stack, expression[i]);
else if (expression[i] == ')' || expression[i] == ']' || expression[i] == '}') {
if (isEmpty(&stack) || !isMatchingPair(pop(&stack), expression[i]))
return 0;
}
}
return isEmpty(&stack);
}
int main() {
char expression[10000];
scanf("%s", expression);
if (isBeautiful(expression))
printf("Yes\n");
else
printf("No\n");
return 0;
}
在这段代码中,我们定义了Node
结构体,用于表示栈中的节点。每个节点包含一个括号字符和一个指向下一个节点的指针。
然后,我们定义了Stack
结构体,用于表示栈。栈包含一个指向栈顶节点的指针。
initializeStack
函数用于初始化栈。
isEmpty
函数用于检查栈是否为空。
push
函数用于将括号字符压入栈中。
pop
函数用于从栈中弹出括号字符。
isMatchingPair
函数用于检查给定的开括号和闭括号是否匹配。
isBeautiful
函数用于判断括号序列是否是美观的。函数遍历给定的括号序列,遇到开括号则将其压入栈中,遇到闭括号则从栈中弹出一个开括号进行匹配。如果遍历完所有字符后,栈为空,则括号序列是美观的。
在main
函数中,我们首先读取输入的括号序列,然后调用isBeautiful
函数判断括号序列是否是美观的。根据判断结果输出"Yes"或"No"。
这样,我们就可以使用栈来判断给定的括号序列是否是美观的。
第4题:中缀表达式的值
人们熟悉的四则运算表达式称为中缀表达式,例如(23+34*45/(5+6+7))。在程序设计语言中,可以利用堆栈的方法把中缀表达式转换成保值的后缀表达式(又称逆波兰表示法),并最终变为计算机可以直接执行的指令,得到表达式的值。 给定一个中缀表达式,编写程序,利用堆栈的方法,计算表达式的值。
时间限制:200
内存限制:65536
输入
第一行为测试数据的组数N 接下来的N行,每行是一个中缀表达式。表达式中只含数字、四则运算符和圆括号,操作数都是正整数,数和运算符、括号之间没有空格。中缀表达式的字符串长度不超过600。
输出
对每一组测试数据输出一行,为表达式的值
样例输入
3
3+5*8
(3+5)*8
(23+34*45/(5+6+7))
样例输出
43
64
108
提示
注意:运算过程均为整数运算(除法运算’/'即按照C++定义的int除以int的结果,测试数据不会出现除数为0的情况),输出结果也为整数(可能为负)。 中间计算结果可能为负。
运行测试
控制台:清理控制台
下面是一个使用栈来计算中缀表达式值的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct {
int capacity;
int top;
int* array;
} Stack;
void initializeStack(Stack* stack, int capacity) {
stack->capacity = capacity;
stack->top = -1;
stack->array = (int*)malloc(stack->capacity * sizeof(int));
}
int isEmpty(Stack* stack) {
return stack->top == -1;
}
int isFull(Stack* stack) {
return stack->top == stack->capacity - 1;
}
void push(Stack* stack, int element) {
if (isFull(stack)) {
printf("Error: Stack is full.\n");
exit(EXIT_FAILURE);
}
stack->array[++stack->top] = element;
}
int pop(Stack* stack) {
if (isEmpty(stack)) {
printf("Error: Stack is empty.\n");
exit(EXIT_FAILURE);
}
return stack->array[stack->top--];
}
int peek(Stack* stack) {
if (isEmpty(stack)) {
printf("Error: Stack is empty.\n");
exit(EXIT_FAILURE);
}
return stack->array[stack->top];
}
int isOperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
int getPrecedence(char operator) {
if (operator == '+' || operator == '-')
return 1;
else if (operator == '*' || operator == '/')
return 2;
else
return 0;
}
int performOperation(int operand1, int operand2, char operator) {
switch (operator) {
case '+':
return operand1 + operand2;
case '-':
return operand1 - operand2;
case '*':
return operand1 * operand2;
case '/':
return operand1 / operand2;
default:
return 0;
}
}
int evaluateExpression(char* expression) {
Stack operandStack;
Stack operatorStack;
initializeStack(&operandStack, strlen(expression));
initializeStack(&operatorStack, strlen(expression));
for (int i = 0; expression[i] != '\0'; i++) {
if (isdigit(expression[i])) {
int operand = 0;
while (isdigit(expression[i])) {
operand = operand * 10 + (expression[i] - '0');
i++;
}
push(&operandStack, operand);
i--;
} else if (expression[i] == '(') {
push(&operatorStack, expression[i]);
} else if (expression[i] == ')') {
while (!isEmpty(&operatorStack) && peek(&operatorStack) != '(') {
int operand2 = pop(&operandStack);
int operand1 = pop(&operandStack);
char operator = pop(&operatorStack);
int result = performOperation(operand1, operand2, operator);
push(&operandStack, result);
}
pop(&operatorStack); // Pop '(' from the operator stack
} else if (isOperator(expression[i])) {
while (!isEmpty(&operatorStack) && getPrecedence(expression[i]) <= getPrecedence(peek(&operatorStack))) {
int operand2 = pop(&operandStack);
int operand1 = pop(&operandStack);
char operator = pop(&operatorStack);
int result = performOperation(operand1, operand2, operator);
push(&operandStack, result);
}
push(&operatorStack, expression[i]);
}
}
while (!isEmpty(&operatorStack)) {
int operand2 = pop(&operandStack);
int operand1 = pop(&operandStack);
char operator = pop(&operatorStack);
int result = performOperation(operand1, operand2, operator);
push(&operandStack, result);
}
return peek(&operandStack);
}
int main() {
int N;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
char expression[601];
scanf("%s", expression);
int result = evaluateExpression(expression);
printf("%d\n", result);
}
return 0;
}
在这段代码中,我们定义了Stack
结构体,用于表示栈。栈包含一个容量、一个指向栈顶的指针以及一个整型数组用于存储栈中的元素。
initializeStack
函数用于初始化栈。
isEmpty
函数用于检查栈是否为空。
isFull
函数用于检查栈是否已满。
push
函数用于将元素压入栈中。
pop
函数用于从栈中弹出元素。
peek
函数用于查看栈顶元素但不弹出。
isOperator
函数用于判断字符是否为运算符。
getPrecedence
函数用于获取运算符的优先级。
performOperation
函数用于执行两个操作数之间的运算。
evaluateExpression
函数用于计算中缀表达式的值。函数遍历给定的中缀表达式,根据运算符的优先级和括号的匹配,使用两个栈(operandStack
和operatorStack
)来计算表达式的值。
在main
函数中,我们首先读取测试数据的组数N
,然后依次读取每组测试数据,调用evaluateExpression
函数计算表达式的值,并将结果输出。
这样,我们就可以使用堆栈的方法计算中缀表达式的值。