算法训练入门模拟专题

数组

1. 【NOIP2005 普及组】校门外的树

题目链接

题目描述

某校大门外长度为 l l l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 1 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 0 0 的位置,另一端在 l l l 的位置;数轴上的每个整数点,即 0 , 1 , 2 , … , l 0,1,2,\dots,l 0,1,2,,l,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式

第一行有两个整数,分别表示马路的长度 l l l 和区域的数目 m m m

接下来 m m m 行,每行两个整数 u , v u, v u,v,表示一个区域的起始点和终止点的坐标。

输出格式

输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。

样例 #1

样例输入 #1

500 3
150 300
100 200
470 471

样例输出 #1

298

提示

【数据范围】

  • 对于 20 % 20\% 20% 的数据,保证区域之间没有重合的部分。
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ l ≤ 1 0 4 1 \leq l \leq 10^4 1l104 1 ≤ m ≤ 100 1 \leq m \leq 100 1m100 0 ≤ u ≤ v ≤ l 0 \leq u \leq v \leq l 0uvl

【题目来源】

NOIP 2005 普及组第二题

题解思路

题目说l的长度最大是10的4次方,区域最多100个,所以可以直接纯暴力也不会超时,建立一个bool数组表示对应下标所表示的树有没有被移走,每次都把区间内的被移走的树对应数组下标值变成true,最后遍历数组统计多少个false就可以了,需要注意的是对于边界的处理可能容易出错。

示例代码

#include <iostream>
using namespace std;

bool tree[10010];

int main() 
{
	int l, m, u, v;
	cin >> l >> m;
	for (int i = 0; i < m; i++) 
	{
		cin >> u >> v;
		for (int j = u; j <= v; j++)
		{
			tree[j] = true;
		}
	}
	int res = 0;
	for (int k = 0; k <= l; k++)
	{
		if (tree[k] == false) res++;
	}
	cout << res;
	return 0;
}

2. 【深基5.习6】蛇形方阵

题目链接

题目描述

给出一个不大于 9 9 9 的正整数 n n n,输出 n × n n\times n n×n
的蛇形方阵。

从左上角填上 1 1 1 开始,顺时针方向依次填入数字,如同样例所示。注意每个数字有都会占用 3 3 3 个字符,前面使用空格补齐。

输入格式

输入一个正整数 n n n,含义如题所述。

输出格式

输出符合题目要求的蛇形矩阵。

样例 #1

样例输入 #1

4

样例输出 #1

 1  2  3  4
12 13 14  5
11 16 15  6
10  9  8  7

提示

数据保证, 1 ≤ n ≤ 9 1 \leq n \leq 9 1n9

题解思路

这是一道十分经典的小模拟题,因为 n n n 最大是9所以看到题解区有直接9个if语句敲了9个矩阵着实看呆了。题解里比较正常的思路就是首先向右走到边界了向下,再到边界向左,再到边界向上,撞到数了向右,又撞到数了向下,所以可以发现每次都会右下左上四个方向循环直到最后一个数停止并且每次撞到边界或者别的数都会转弯。所以我们代码里可以用四个while语句按右下左上的顺序每个循环里面判断到边界或者别的数就结束进入下一个,可以看看代码和注释理解一下。

示例代码

#include<iostream>
using namespace std;

int a[15][15];

int main() {
	int n; cin >> n;
	int cnt = 0, x = 1, y = 0;// cnt记录现在走到哪个数,x和y表示坐标

	while (cnt < n * n) {//从头开始,每次循环记录一个数,最后一个数是n * n,记录到最后一个了就跳出
		// 向右看那个数不越界并且不撞到别的数就走过去更新数组上该位置的值
		while (y < n && a[x][y + 1] == 0) a[x][++y] = ++cnt;

		// 向下看那个数不越界并且不撞到别的数就走过去更新数组上该位置的值
		while (x < n && a[x + 1][y] == 0) a[++x][y] = ++cnt;

		// 向左看那个数不越界并且不撞到别的数就走过去更新数组上该位置的值
		while (y > 1 && a[x][y - 1] == 0) a[x][--y] = ++cnt;

		// 向下看那个数不越界并且不撞到别的数就走过去更新数组上该位置的值
		while (x > 1 && a[x - 1][y] == 0) a[--x][y] = ++cnt;
	}

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			printf("%3d", a[i][j]);
		}
		printf("\n");
	}
	return 0;
}

3. 【Mc生存】插火把

原题链接

题目背景

初一党应该都知道…

题目描述

话说有一天 linyorson 在“我的世界”开了一个 n × n n \times n n×n 的方阵,现在他有 m m m 个火把和 k k k 个萤石,分别放在 ( x 1 , y 1 ) ∼ ( x m , y m ) (x_1, y_1) \sim (x_m, y_m) (x1,y1)(xm,ym) ( o 1 , p 1 ) ∼ ( o k , p k ) (o_1, p_1) \sim (o_k, p_k) (o1,p1)(ok,pk) 的位置,没有光并且没放东西的地方会生成怪物。请问在这个方阵中有几个点会生成怪物?

P.S. 火把的照亮范围是:

    |暗|暗| 光 |暗|暗|
    |暗|光| 光 |光|暗|
    |光|光|火把|光|光|
    |暗|光| 光 |光|暗|
    |暗|暗| 光 |暗|暗|

萤石:

    |光|光| 光 |光|光|
    |光|光| 光 |光|光|
    |光|光|萤石|光|光|
    |光|光| 光 |光|光|
    |光|光| 光 |光|光|

输入格式

输入共 m + k + 1 m + k + 1 m+k+1 行。
第一行为 n , m , k n, m, k n,m,k
2 2 2 到第 m + 1 m + 1 m+1 行分别是火把的位置 x i , y i x_i, y_i xi,yi
m + 2 m + 2 m+2 到第 m + k + 1 m + k + 1 m+k+1 行分别是萤石的位置 o i , p i o_i, p_i oi,pi

注:可能没有萤石,但一定有火把。

输出格式

有几个点会生出怪物。

样例 #1

样例输入 #1

5 1 0
3 3

样例输出 #1

12

提示

数据保证, 1 ≤ n ≤ 100 1 \le n \le 100 1n100 1 ≤ m + k ≤ 25 1 \leq m+k \leq 25 1m+k25 1 ≤ m ≤ 25 1 \leq m \leq 25 1m25 0 ≤ k ≤ 5 0 \leq k \leq 5 0k5

题解思路

这道题是个小模拟题,因为矩阵边长只有100, m + k m + k m+k 的值也很小,纯暴力是完全可以的,所以我们只需要建立一个二维的bool数组表示对应位置上有没有光,初始二维数组中都是false,每次把火把或者萤石周围对应位置的值改为true,最后统计矩阵中false的数量即可。需要注意的是这道题的边界情况还是不太好处理的,如果直接从矩阵左上角开始的话,火把或者萤石上面和左边也要改成true,这样会越界,另外也需要注意题上矩阵是从(1,1)开始的,而二维数组都是从(0,0)开始的。处理这几个问题方法也不止一种,这里我直接让矩阵向右下方平移两格,这样最后统计时只需要从原来的(3,3)开始就行,外面的不会计算,可以参考一下图和代码理解。在这里插入图片描述

示例代码

#include<iostream>
using namespace std;

bool a[110][110];
int cnt;

int main() {
    int n, m, k, x, y; cin >> n >> m >> k;
    for (int i = 0; i < m; i++) {
        cin >> x >> y;
        x += 2, y += 2;
        for (int i = x - 1; i <= x + 1; i++)
            for (int j = y - 1; j <= y + 1; j++)
                a[i][j] = true;
        a[x - 2][y] = true, a[x + 2][y] = true, a[x][y + 2] = true, a[x][y - 2] = true;
    }
    for (int i = 0; i < k; i++) {
        cin >> x >> y;
        x += 2, y += 2;
        for (int i = x - 2; i <= x + 2; i++)
            for (int j = y - 2; j <= y + 2; j++)
                a[i][j] = true;
    }
    for (int i = 3; i <= n + 2; i++)
        for (int j = 3; j <= n + 2; j++)
            if (a[i][j] == false) cnt++;
    cout << cnt;
}

字符串

1. 【NOIP2008 提高组】 笨小猴

原题链接

题目描述

笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!

这种方法的具体描述如下:假设 maxn \text{maxn} maxn 是单词中出现次数最多的字母的出现次数, minn \text{minn} minn 是单词中出现次数最少的字母的出现次数,如果 maxn − minn \text{maxn}-\text{minn} maxnminn 是一个质数,那么笨小猴就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。

输入格式

一个单词,其中只可能出现小写字母,并且长度小于 100 100 100

输出格式

共两行,第一行是一个字符串,假设输入的的单词是 Lucky Word,那么输出 Lucky Word,否则输出 No Answer

第二行是一个整数,如果输入单词是 Lucky Word,输出 maxn − minn \text{maxn}-\text{minn} maxnminn 的值,否则输出 0 0 0

样例 #1

样例输入 #1

error

样例输出 #1

Lucky Word
2

样例 #2

样例输入 #2

olympic

样例输出 #2

No Answer
0

提示

【输入输出样例 1 解释】

单词 error 中出现最多的字母 r \texttt r r 出现了 3 3 3 次,出现次数最少的字母出现了 1 1 1 次, 3 − 1 = 2 3-1=2 31=2 2 2 2 是质数。

【输入输出样例 2 解释】

单词 olympic 中出现最多的字母 i \texttt i i 出现了 1 1 1 次,出现次数最少的字母出现了 1 1 1 次, 1 − 1 = 0 1-1=0 11=0 0 0 0 不是质数。

(本处原题面错误已经修正)

noip2008 提高第一题

题解思路

这道题只需要我们遍历数组分别求出来出现最多的字母的次数 m a x n maxn maxn和最少的字母的次数 m i n n minn minn,遍历时可以用一个长度为26的int数组存每个字母出现的次数,再遍历一遍这个数组找到其中的最大最小值即可。再判断 m a x n − m i n n maxn - minn maxnminn的值是不是质数即可。

示例代码

#include <bits/stdc++.h>
using namespace std;

int cnt[26];

// 判断是否是质数
bool isPrime(int x) {
	if (x < 2) return false;
	for (int i = 2; i <= x / i; i++) {
		if (x % i == 0) return false;
	}
	return true;
}

int main() {
	string s; cin >> s;
	int maxn = 0, minn = 100;
	for (int i = 0; i < s.size(); i++) {
		cnt[s[i] - 'a']++;
	}
	for (int i = 0; i < 26;i ++) {
		maxn = max(cnt[i], maxn);
		if (cnt[i] == 0) continue;// 没有出现过的字母自然不算数
		minn = min(cnt[i], minn);
	}
	int res = maxn - minn;
	if (isPrime(res)) cout << "Lucky Word" << endl << res;
	else cout << "No Answer" << endl << 0;
	return 0;
}

2. 斯诺登的密码

原题链接

题目背景

根据斯诺登事件出的一道水题

题目描述

2013 年 X 月 X 日,俄罗斯办理了斯诺登的护照,于是他混迹于一架开往委内瑞拉的飞机。但是,这件事情太不周密了,因为 FBI 的间谍早已获悉他的具体位置——但这不是最重要的——最重要的是如果要去委内瑞拉,那么就要经过古巴,而经过古巴的路在美国的掌控之中。

丧心病狂的奥巴马迫降斯诺登的飞机,搜查时却发现,斯诺登杳无踪迹。但是,在据说是斯诺登的座位上,发现了一张纸条。纸条由纯英文构成:Obama is a two five zero.(以 . 结束输出,只有 6 6 6 个单词+一个句号,句子开头如没有大写亦为合法)这句话虽然有点无厘头,但是警官陈珺骛发现这是一条极其重要的线索。他在斯诺登截获的一台笔记本中找到了一个 C++ 程序,输入这条句子后立马给出了相对应的密码。陈珺鹜高兴得晕了过去,身为警官的你把字条和程序带上了飞机,准备飞往曼哈顿国际机场,但是在飞机上检查的时候发现——程序被粉碎了!飞机抵达华盛顿只剩 5 5 5 分钟,你必须在这 5 5 5 分钟内编写(杜撰)一个程序,免受上司的 10000000000   m o d   10 10000000000 \bmod 10 10000000000mod10 大板。破译密码的步骤如下:

(1)找出句子中所有用英文表示的数字 ( ≤ 20 ) (\leq 20) (20),列举在下:

正规:one two three four five six seven eight nine ten eleven twelve
thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty

非正规:a both another first second third。为避免造成歧义,another 算作 1 1 1 处理。

(2)将这些数字平方后对 100 100 100 取模,如 00 , 05 , 11 , 19 , 86 , 99 00,05,11,19,86,99 00,05,11,19,86,99

(3)把这些两位数按数位排成一行,组成一个新数,如果开头为 0 0 0,就去 0 0 0

(4)找出所有排列方法中最小的一个数,即为密码。

输入格式

一个含有 6 6 6 个单词的句子。

输出格式

一个整型变量(密码)。如果没有符合要求的数字出现,则输出 0 0 0

样例 #1

样例输入 #1

Black Obama is two five zero .

样例输出 #1

425

题解思路

如题主所说,水题模拟,个人感觉坑还不少,写了个屎山代码还是参考了一下别人的,看到别的大佬题解里面有用map的或者开俩数组一个int存值一个string存字符串比这个代码短一点,不过思路都是打表。
思路就是六个小于20的单词,每个单词符合是数字的要求就平方一下模100最后求拼接起来的最小值,因为让求数字拼起来的最小值,所以有0的话肯定不会放在后面让它增加长度的,所以0肯定会被去除,所以0的情况就不用管。然后就是sort按从小到大排一下序,个位数的话要在前面补零(一开始没看清使劲WA真难受),因为会补0所以肯定会比二位数小,开头的如果是个位是不可以补0的(我在这就忘考虑了),所以我们先输出开头的数就行,最后不要忘了特殊情况判断一下没有符合的单词直接输出0(在这又WA了,一道水题调了俩仨小时,被自己菜哭了)。

示例代码

#include<bits/stdc++.h>
using namespace std;

vector<int> v;

int count(string s) {
    if (s == "one" || s == "a" || s == "first" || s == "another") v.push_back(1);
    if (s == "two" || s == "both" || s == "second") v.push_back(4);
    if (s == "three" || s == "third") v.push_back(9);
    if (s == "four") v.push_back(16);
    if (s == "five") v.push_back(25);
    if (s == "six") v.push_back(36);
    if (s == "seven") v.push_back(49);
    if (s == "eight") v.push_back(64);
    if (s == "nine") v.push_back(81);
    if (s == "eleven") v.push_back(11 * 11 % 100);
    if (s == "twelve") v.push_back(12 * 12 % 100);
    if (s == "thirteen") v.push_back(13 * 13 % 100);
    if (s == "fourteen") v.push_back(14 * 14 % 100);
    if (s == "fifteen")  v.push_back(15 * 15 % 100);
    if (s == "sixteen") v.push_back(16 * 16 % 100);
    if (s == "seventeen") v.push_back(17 * 17 % 100);
    if (s == "eightteen") v.push_back(18 * 18 % 100);
    if (s == "nineteen") v.push_back(19 * 19 % 100);
}

int main() {
    string s, p;
    getline(cin, s);
    stringstream ss(s);// 这个函数用来去除空格
    while (ss >> p) {
        if (p[p.size() - 1] == '.') p.erase(p.size() - 1);
        count(p);
    }
    sort(v.begin(), v.end());
    if (v.empty()) cout << 0;
    else {
    	cout << v[0];
    	for (int i = 1; i < v.size(); i++) {
    		if (v[i] < 10) cout << 0;
   		 	cout << v[i];
		}
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值