2022暑初二信息竞赛学习成果分享1

学习目录1


2022暑初二信息竞赛学习成果分享系列

上一篇 这一篇 下一篇
2022暑初二信息竞赛学习成果分享1 2022暑初二信息竞赛学习成果分享2

第一期 (2022/07/11~2022/07/16)

Day 1:复习——STL、二叉堆、二维费用背包

Morning——STL复习

新的一天从#include <cstdio>开始。 ——C2024XSC249

T1. 车站铁轨

题目描述
n n n 节车厢从 A 方向驶入车站,按进站顺序编号为 1... n 1...n 1...n

你的任务是让他们按照某中特定的顺序进入 B 方向的铁轨并驶出车站。为了重组车厢,你可以借助中转站 C。这是一个可以停放任意多节车厢的车站,但由于末端封顶,驶入 C 的车厢必须按照相反的顺序驶出 C。对于每个车厢,一旦从 A 移入 C,就不能再回到 A 了;一旦从 C 移入 B,就不能回到 C 了。换句话说,在任意时刻,只有两种选择:A->C 和 C->B。

现在需要你写一个程序,判断给定的 B 方向驶出车站的车箱顺序是否可行,若不可行输出 no;若可行则输出 yes,并输出要能得到这个出站顺序,中转站 C 至少需要几个存放车厢的位置。

输入格式
1 1 1 行一个整数 n n n,表示有 n n n 节车厢;
2 2 2 n n n 个整数,是 1... n 1...n 1...n 的排列,表示 B 方向驶出的车厢顺序。

输出格式
若 B 方向出站车厢顺序不可行输出 no,若可行,则输出 yes,并在第二行输出中转站至少要提供车厢位置数。

样例
输入样例

5
1 2 3 4 5

输出样例

yes
1

数据范围与提示
n ≤ 10000 n \le 10000 n10000

理解与感悟

一看这个火车轨道,就想到栈。

栈中先存 1... n 1...n 1...n的排列。每枚举到一个放一个即可。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;

int main () {
   
	int n, x, a[10005], step = 1, ans = 1;
	//a数组表示放车的顺序,step表示现在正在放的车的编号。
	stack <int> s;
	scanf ("%d", &n);
	for (int i = 1; i <= n; i ++) {
   
		scanf ("%d", &a[i]);
	}
	for (int i = 1; i <= n; i ++) {
   
		s.push (i);
		ans = max (ans, (int) s.size());
		while (s.size()) {
   
			if (s.top() == a[step]) {
   //可以放车就pop
				step ++;
				s.pop();
			} else {
   
				break;
			}
		}
	}
	if (s.size()) {
   //输出,注意yes后还要写ans。
		printf ("no");
	} else {
   
		printf ("yes\n");
		printf ("%d", ans);
	}
	return 0;
}
T2. 简单计算器

题目描述
读入一个只包含+、-、*、/、的非负整数计算表达式,计算该表达式的值。

输入格式
测试数据有多组,每组占一行。

每行不超过200个字符,整数和运算符之间用一个空格分隔。

没有非法表达式。

当一行中只有一个0时,表示输入结束,相应的结果不要输出。

输出格式
对于每组测试数据输出一行,即该表达式的值,精确到小数点后两位。

样例
输入样例

30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0

输出样例

12178.21
理解与感悟

也是用栈进行模拟。

由于输入当中含空格,所以要注意过滤空格。

先输入第一个数和第一个空格。

如果第一个数是 0 0 0且不是空格,就表示输入结束,结束程序。

否则,就每一次已符号+' '+数+' '的格式进行输入:

  • 如果符号为+,直接压入栈;
  • 如果符号为-,将相反数压入栈;
  • 如果符号为*或/,则将栈顶元素取出,乘除操作后再压入。

每行操作完后,栈中的元素相当于原式的代数和,最后累加这些代数和即可。

代码

#include <cstdio>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;

stack <double> s;
double a, p;
char b, k;

int main () {
   
    while (scanf ("%lf", &a) == 1) {
   //第一个数+' '
        k = getchar ();
        if (a == 0 && k != ' ') {
   
            break;
        }
        s.push(a);
        while (~scanf ("%c%lf", &b, &a)) {
   //符号+' '+数+' '
            k = getchar ();
            if (b == '*') {
   
                p = s.top() * a;
                s.pop();
                s.push(p);
            } else if (b == '/') {
   
                p = s.top() / a;
                s.pop();
                s.push(p);
            } else {
   
                if (b == '-') {
   
                    a = -a;
                }
                s.push(a);
            }
            if (k != ' ') {
   
                break;
            }
        }
        double ans = 0;
        while (s.size()) {
   //累加
            ans += s.top();
            s.pop();
        }
        printf ("%.2lf\n", ans);
    }
    return 0;
}
T3. 括号平衡

题目描述
在本题中,题目会先给你一个包含小括号()及中括号[]的字串。当字串符合下列条件时我们称它为正确的运算式:

  1. 该字串为一个空字串。

  2. 如果 A 和 B 都为正确的运算式,则 AB 也为正确的运算式。

  3. 如果 A 为正确的运算式,则 (A) 及 [A] 都为正确的运算式。

现在,请你写一支程序可以读入这类字串并检查它们是否为正确的运算式。字串的长度不超过 128 128 128

输入格式
第一行为正整数 n n n,代表接下来有 n n n 个字符串。

接下来的 n n n 行,每行是一个仅含小括号和大括号的字符串(长度不大于 10000 10000 10000)。

输出格式
针对每个输入的括号字符串,如果是正确的运算式,则输出 Yes,否则输出 No

样例
输入样例

4 
([])
(([()])))
([()[]()])()
([)]

输出样例

Yes
No
Yes
No

数据范围与提示
1 ≤ n ≤ 100 1 \leq n \leq 100 1n100

理解与感悟

还是用栈进行模拟。

  • 如果输入的字符能与栈顶字符匹配,则pop
  • 否则压进栈。

若最后栈内没有元素,输出Yes
否则输出No

代码

#include <cstdio>
#include <stack>
#include <algorithm>
#include <cstring>
using namespace std;

int n, len;
stack <char> s;
char a[10005];

bool match (char a, char b) {
   
	return (a == '(' && b == ')') || (a == '[' && b == ']');
}

int main () {
   
	scanf ("%d", &n);
	while (n --) {
   
		scanf ("%s", a);
		len = strlen (a);
		for (int i = 0; i < len; i ++) {
   
			if (s.size() && match (s.top(), a[i])) {
   //可以匹配,pop
				s.pop();
			} else {
   
				s.push(a[i]);
			}
		}
		if (s.size()) {
   //还有东西,输出No
			printf ("No\n");
			while (s.size()) {
    s.pop(); }//清空栈,为下一个数据做准备
		} else {
   
			printf ("Yes\n");
		}
	}
	return 0;
}
T4. 卡片游戏

题目描述
桌子上有一叠牌,从第一张牌(即位于顶面的牌)开始从上到下依次编号为 1... n 1...n 1...n,当至少还剩下两张牌时进行以下操作:把第一张扔掉,然后把新的一张放到整叠牌的最后。
输入 n n n,输出每次扔掉的牌,以及最后剩下的牌。

输入格式
第一行是一个整数 n n n

输出格式
一行 n n n 个整数,按顺序输出扔掉的 n − 1 n-1 n1 张牌的序号和最后剩牌的序号。

样例
输入样例

7

输出样例

1 3 5 7 4 2 6

数据范围与提示
n ≤ 20000 n \le 20000 n20000

理解与感悟

按照题目要求用栈模拟即可。

  • 先输入并取出。
  • 在把这时顶上的加在后面去,并删除。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

queue <int> q;
int n;

int main () {
   
	scanf ("%d", &n);
	for (int i = 1; i <= n; i ++) {
   
		q.push(i);
	}
	while (q.size() >= 2) {
   
		printf ("%d ", q.front());
		q.pop();
		q.push(q.front());
		q.pop();
	}
	printf ("%d", q.front());
	return 0;
}
T5. 队列及其操作

题目描述
队列(queue):在线性表的一端插入元素,在另一端删除元素,所以遵循先进先出( FIFO)原则,元素从队尾进,队首出,不允许插队!

其中删除元素的一端称为队首(front),插入元素的一端称为队尾(rear)。

队列就像我们排队打饭,先来的先打饭,后来的只能排在队尾。

输入格式
第1行包含一个整数 n n n,表示有 n n n 条关于 queue 的操作,在进行任何操作之前,queue 都是空的。接来的 n n n 行,每行是一个关于 queue 的操作,格式和含义如下:
clear:把队列置空。

empty:判断队列是否为空。

push:把整数 x x x 插入队尾( x x x 为 int 范围里的数)。

pop: 队首元素出队列。

front:获取队首元素的值。

输出格式
对于 front 操作,输出一个整数,如果这个操作失败,则输出单词 error

对于 pop 操作,如果这个操作失败,则输出单词 error

对于 empty 操作,如果队列是空,则输出 empty,否则输出 not empty

样例
输入样例

8
push 10
front 
push 15
pop
front 
clear
front
pop

输出样例

10
15
error
error

数据范围与提示

n ≤ 20000 n \le 20000 n20000

理解与感悟

按照题目的相应方式调用queue中的成员函数即可。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

queue <int> q;
int n, k;
char str[15];

int main () {
   
	scanf ("%d", &n);
	for (int i = 1; i <= n; i ++) {
   
		scanf ("%s", str);
		if (strcmp (str, "push") == 0) {
   
			scanf ("%d", &k);
			q.push(k);
		} else if (strcmp (str, "pop") == 0) {
   
			if (q.size() == 0) {
   
				printf ("error\n");
			} else {
   
				q.pop();
			}
		} else if (strcmp (str, "front") == 0) {
   
			if (q.size() == 0) {
   
				printf ("error\n");
			} else {
   
				printf ("%d\n", q.front());
			}
		} else if (strcmp (str, "empty") == 0) {
   
			if (q.empty()) {
   
				printf ("empty\n");
			} else {
   
				printf ("not empty\n");
			}
		} else if (strcmp (str, "clear") == 0) {
   
			while (q.size()) {
   
				q.pop();
			}
		}
	}
	return 0;
}
T6. 机器翻译

题目描述
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。

这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有 M M M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M − 1 M-1 M1 ,软件会将新单词存入一个未使用的内存单元;若内存中已存入 M M M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为 N N N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

输入格式
输入共 2 2 2 行。每行中两个数之间用一个空格隔开。

第一行为两个正整数 M M M N N N,代表内存容量和文章的长度。

第二行为 N N N 个非负整数,按照文章的顺序,每个数(大小不超过 1 , 000 1,000 1,000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

输出格式
输出共 1 1 1 行,包含一个整数,为软件需要查词典的次数。

样例
样例输入 1

3 7
1 2 1 5 4 4 1

样例输出 1

5

样例 1 说明
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:

空:内存初始状态为空。

  1. 1:查找单词 1 1 1 并调入内存;
  2. 1 2:查找单词 2 2 2 并调入内存;
  3. 1 2:在内存中找到单词 1 1 1
  4. 1 2 5:查找单词 5 5 5 并调入内存;
  5. 2 5 4 :查找单词 4 4 4 并调入内存替代单词 1 1 1
  6. 2 5 4:在内存中找到单词 4 4 4
  7. 5 4 1:查找单词 1 1 1 并调入内存替代单词 2 2 2
    共计查了 5 5 5 次词典。

样例输入 2

2 10
8 824 11 78 11 78 11 78 8 264

样例输出 2

6

数据范围与提示
对于 10 % 10\% 10% 的数据有 M = 1 M=1 M=1 N ≤ 5 N \le 5 N5
对于 100 % 100\% 100% 的数据有 0 ≤ M ≤ 100 0 \le M \le 100 0M100 0 ≤ N ≤ 1000 0 \le N \le 1000 0N1000

理解与感悟

用队列进行模拟,可以用map进行映射,也可以用一个bool数组来标记。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;

queue <int> q;
map <int, bool> mp;//second==1,队列中有first元素

int m, n, x, ans;

int main () {
   
	scanf ("%d %d", &m, &n);
	for (int i = 1; i <= n; i ++) {
   
		scanf ("%d", &x);
		if (mp[x] == false) {
   
			mp[x] = true;
			q.push(x);
			if (q.size() > m) {
   //空间爆了,向前递补。
				mp[q.front()] = false;
				q.pop();
			}
			ans ++;
		}
	}
	printf ("%d", ans);
	return 0;
}
T7. 公交换乘

题目描述

著名旅游城市 B 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案:

在搭乘一次地铁后可以获得一张优惠票,有效期为 45 分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。在有效期内指 开始乘公交车的时间与开始乘地铁的时间之差小于等于 45 分钟,即: t b u s − t s u b w a y ≤ 45 t_{bus}-t_{subway} \le 45 tbustsubway45

搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优惠票搭乘公交车。

搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满足条件,则优先消耗获得最早的优惠票。

现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗?

输入格式

输入文件的第一行包含一个正整数 n n n,代表乘车记录的数量。

接下来的 n n n 行,每行包含 3 个整数,相邻两数之间以一个空格分隔。第 i i i 行的 第 1 个整数代表第 𝑖 条记录乘坐的交通工具,0 代表地铁,1 代表公交车;第 2 个 整数代表第 i i i 条记录乘车的票价 p r i c e i price_i pricei; 第三个整数代表第 i i i 条记录开始乘车的时 间 t i t_i ti(距 0 时刻的分钟数)。

我们保证出行记录是按照开始乘车的时间顺序给出的,且不会有两次乘车记录出现 在同一分钟。

输出格式

输出文件有一行,包含一个正整数,代表小轩出行的总花费

样例

样例1输入

6 
0 10 3 
1 5 46 
0 12 50 
1 3 96 
0 5 110 
1 6 135

样例1输出

36

第一条记录,在第 3 分钟花费 10 元乘坐地铁。 第二条记录,在第 46 分钟乘坐公交车,可以使用第一条记录中乘坐地铁获得的优 惠票,因此没有花费。 第三条记录,在第 50 分种花费 12 元乘坐地铁。 第四条记录,在第 96 分钟乘坐公交车,由于距离第三条记录中乘坐地铁已超过 45 分钟,所以优惠票已失效,花费 3 元乘坐公交车。 第五条记录,在第 110 分钟花费 5 元乘坐地铁。 第六条记录,在第 135 分钟乘坐公交车,由于此时手中只有第五条记录中乘坐地铁 获得的优惠票有效,而本次公交车的票价为 6 元,高于第五条记录中地铁的票价 5 元, 所以不能使用优惠票,花费 6 元乘坐公交车。 总共花费 36 元。

样例2输入

6 
0 5 1 
0 20 16 
0 7 23 
1 18 31 
1 4 38 
1 7 68

样例2输出

32

第一条记录,在第 1 分钟花费 5 元乘坐地铁。 第二条记录,在第 16 分钟花费 20 元乘坐地铁。 第三条记录,在第 23 分钟花费 7 元乘坐地铁。 第四条记录,在第 31 分钟乘坐公交车,此时只有第二条记录中乘坐的地铁票价高 于本次公交车票价,所以使用第二条记录中乘坐地铁获得的优惠票。 第五条记录,在第 38 分钟乘坐公交车,此时第一条和第三条记录中乘坐地铁获得 的优惠票都可以使用,使用获得最早的优惠票,即第一条记录中乘坐地铁获得的优惠票。 第六条记录,在第 68 分钟乘坐公交车,使用第三条记录中乘坐地铁获得的优惠票。 总共花费 32 元。

数据范围与提示

对于 30 % 30\% 30% 的数据, n ≤ 1000 , t i ≤ 1 0 6 n \le 1000, t_i \le 10^6 n1000,ti106

另有 15 % 15\% 15% 的数据, t i ≤ 1 0 7 ti \le 10^7 ti107 p r i c e i price_i pricei都相等。

另有 15 % 15\% 15% 的数据, t i ≤ 1 0 9 t_i \le 10^9 ti109 p r i c e i price_i pricei都相等。

对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 , t i ≤ 1 0 9 , 1 ≤ p r i c e i ≤ 1000 n \le 10^5, t_i \le 10^9, 1 \le price_i \le 1000 n105,ti109,1pricei1000

理解与感悟

我们用一个队列来存优惠票的时间。

每一次使用优惠票的时候,都清除一下作废的票,最后统计结果即可。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;

int n, h, p, t, ans;

struct node {
   
	int t, p;
};
queue <node> q, q1;

int main () {
   
	scanf ("%d", &n);
	while (n --) {
   
		scanf ("%d %d %d", &h, &p, &t);
		if (h == 0) {
   
			q.push((node) {
   t + 45, p});
			ans += p;
		} else {
   
			while (q.size()) {
   //清除作废的票
				if (t - q.front().t > 0) {
   
					q.pop();
				} else {
   
					break;
				}
			}
			if (q.size() > 0) {
   
				bool f = 0;
				while (q.size()) {
   
					if (q.front().p >= p) {
   
						q.pop();
						f = 1;
						break;
					} else {
   
						q1.push(q.front());
						q.pop();
					}
				} 
				while (q.size()) {
   
					q1.push(q.front());
					q.pop();
				}
				while (q1.size()) {
   //用q1作为temp队列,用于存储临时元素
					q.push(q1.front());
					q1.pop();
				}
				if (f == 0) {
   
					ans += p;
				}
			} else {
   
				ans += p;
			}
		}
	}
	printf ("%d", ans);
	return 0;
}
T8. 合并果子

题目描述

一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入格式

输入包括两行。

第一行是一个整数 n ( 1 ≤ n ≤ 10000 ) n(1 \le n \le 10000) n(1n10000),表示果子的种类数。

第二行包含 n n n个整数,用空格分隔,第 i i i个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1 \le a_i \le 20000) ai(1a

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值