PAT乙级程序设计题目解析(前50题)

660 阅读21分钟

注释:该解析不放置输入输出格式和输入输出样例,详情请参考PAT官网题目库。且我在解题之后还会参考柳神的代码做比较,学习好的部分,提升自己的代码。这里附上柳神的pat的github,有兴趣的小伙伴可去自行下载。github.com/liuchuo/PAT


1001 害死人不偿命的(3n+1)猜想(15分)

卡拉兹(Callatz)猜想:对任何一个正整数 n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把 (3n+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 n=1。卡拉兹在 1950 年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证 (3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展…… 我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过 1000 的正整数 n,简单地数一下,需要多少步(砍几下)才能得到 n=1?

解析:

做简单的逻辑梳理,分为奇数和偶数两种情况去处理即可。

代码:

#include <iostream>
using namespace std;
int main() {
    int n, count = 0;
    cin >> n;
    while (n != 1) {
        if (n % 2 != 0) n = 3 * n + 1;
        n = n / 2;
        count++;
    }
    cout << count;
    return 0;
}

1002 写出这个数 (20 分)

读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字。

解析:

  1. 计算各个位数的和
  2. 用汉语拼音写出和的每一位数字 显然我们这里要计算每一位数字的和,可以直接用string类型输入,然后遍历每一位上的数进行相加。将得到的结果再进行遍历,设置一个拼音数组,将遍历出来的每一位的结果在拼音数组中查找然后放置到结果字符串中,注意格式,最后输出。 这里遍历string类型的变量有几种方式:
  • for循环
string n;
for(int i =  0; i < n.size() ;i++){
    cout << n[i]
}
  • 升级版for循环(底层是由迭代器完成的)
string n;
//用ch依次取n里面的字符,直到取完为止
for(auto ch :n){
    cout << ch;
}
  • 使用迭代器遍历字符串
string n;
string::iterator it = n.begin();
while(it != n.end()){
    cout << *it;
    it++;
}

这里还有一个难点就是string转换成整数,用一个简单的方法

string str = "123";
int n;
n = str - '0';//n=123,这里利用ASCLL字符编码规律去转换,是一个非常便捷的方法

代码:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string n;
	string result;
	string array[10] = { "ling","yi","er" ,"san" ,"si" ,"wu" ,"liu" ,"qi" ,"ba" ,"jiu" };
	int t;

	cin >> n;
	int sum = 0;
	for (int i = 0; i < n.size(); i++) {
		sum += n[i] - '0';
	}
	while (sum != 0) {
		t = sum % 10;
		if (result == "") {
			result = array[t];
		}
		else {
			result = array[t] + " " + result;

		}
		sum = sum / 10;
	}
	cout << result;

	return 0;
}

1003 我要通过! (20 分)

答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。

得到“答案正确”的条件是:

  1. 字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符;
  2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
  3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a、 b、 c 均或者是空字符串,或者是仅由字母 A 组成的字符串。

现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。

解析:

这道题颇有难度。我们先分析三个条件。条件一:表示必须有且仅有P A T三种字符。条件二:满足xPATx这种左右两边对称形式即可。条件三:是条件一和条件二的增强,这里其实是一个迭代规律,假如AAPATAA根据条件二成立,则根据条件三AAPAATAAAA成立,则再根据条件三AAPAAATAAAAAA成立。这里通过找规律可以发现,中间A的个数和前面A的个数的乘积是后面A的个数。找到规律,即可解决。

代码

#include <iostream>
#include <string>
using namespace std;

int main() {
	int n, a, b, c, aa;
	int sign = 0;
	string::size_type position_p, position_t, position_p_last, position_t_last;
	string str;
	cin >> n;
	while (n != 0) {
		sign = 0;
		cin >> str;
		for (int i = 0; i < str.size(); i++) {
			if (str[i] != 'A'&&str[i] != 'P'&&str[i] != 'T') {
				sign = 1;
				goto stop;
			}

		}

		position_p = str.find_first_of("P");
		position_t = str.find_first_of("T");
		if (position_p == str.npos || position_t == str.npos) {
			sign = 1;
			goto stop;
		}
		position_p_last = str.find_last_of("P");
		position_t_last = str.find_last_of("T");

		if (position_p != position_p_last || position_t != position_t_last) {
			sign = 1;
			goto stop;
		}
		a = position_p;
		b = position_t - position_p - 1;
		c = str.size() - position_t - 1;
		if (b == 0) {
			sign = 1;
			goto stop;
		}
		if (a*b != c) {
			sign = 1;
			goto stop;
		}

	stop:if (sign == 1)cout << "NO" << endl;
		if (sign == 0) cout << "YES" << endl;
		n--;


	}
	return 0;
}

1004 成绩排名 (20 分)

读入 n(>0)名学生的姓名、学号、成绩,分别输出成绩最高和成绩最低学生的姓名和学号。

解析:

这道题目相对简单。但是我看到这题的第一反应是三元组,结构体,哈希表,数组等方式,把简单的问题复杂化了。其实这道题只是要求找最大最小值而已,且元素内容简单,用标志法做记录就可以找到。

代码:

#include <iostream>
#include <string>
using namespace std;

int main() {
	int n;
	cin >> n;
	string name, code;
	string max_name, max_code;
	string min_name, min_code;
	int socure, max_source, min_source;
	max_source = 0;
	min_source = 100;
	for (int i = 0; i < n; i++) {
		cin >> name >> code >> source;
		if (source > max_source) {
			max_name = name;
			max_code = code;
			max_source = source;
		}
		if (source < min_source) {
			min_name = name;
			min_code = code;
			min_source = source;
		}
	}
	cout << max_name << " " << max_code << endl;
	cout << min_name << " " << min_code;
}

1005 继续(3n+1)猜想 (25 分)

卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。

当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n=3 进行验证的时候,我们需要计算 3、5、8、4、2、1,则当我们对 n=5、8、4、2 进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这 4 个数已经在验证3的时候遇到过了,我们称 5、8、4、2 是被 3“覆盖”的数。我们称一个数列中的某个数 n 为“关键数”,如果 n 不能被数列中的其他数字所覆盖。

现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。

解析:

这道题是稍微有点难度的。第一反应是用循环然后给数组做标记。这个思路是正确的,但是如何高效的实现值得我们思考。这题参考了柳神的解法。引入了vector动态数组以及algorithm中的sort函数,以及在格式化输出的时候巧妙的运用了flag做空格标记。这两个方法在我之前是没有经常使用的,但是看了大神的代码确实学到不少,这里我po出柳神的代码。

代码:

#include <iostream>
#include <vector>
#include <algorithm>
//这里引入动态数组

bool cmp(int a, int b) { return a > b; }

using namespace std;
int arr[10000];

int main() {
	int k,n,flag = 0;

	cin >> k;
	vector<int> v(k);
	for (int i = 0; i < k; i++) {
		cin >> n;
		v[i] = n;
		while (n != 1) {
			if (n % 2 != 0) n = 3 * n + 1;
			n = n / 2;
			if (arr[n] == 1) break;
			arr[n] = 1;
		}

	}
	sort(v.begin(), v.end(), cmp);
	for (int i = 0; i < v.size(); i++) {
		if (arr[v[i]] == 0) {
			if (flag == 1) cout << " ";
			cout << v[i];
			flag = 1;
		}
	}
	return 0;

}

1006 换个格式输出整数 (15 分)

让我们用字母 B 来表示“百”、字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正整数。例如 234 应该被输出为 BBSSS1234,因为它有 2 个“百”、3 个“十”、以及个位的 4。

解析:

这道题可以说是很简单了,只要分析不同位数,然后输出就好了。虽然思路很简单,但是要把思路转化成一种优雅的代码还是很需要能力的。我对比了自己的代码和柳神的代码,嗷呜人家的不要太简洁,对比可以看出我的思维还是有些冗杂的,emmm自我分析应当是对c++使用不熟练导致的。因此我需要多向好的代码学习。下面我附上自己的代码和柳神的代码。

代码(我的):

#include <iostream>
#include <string>
using namespace std;
int main() {
	int n, data, k = 1;
	string result, str_data;
	cin >> n;
	while (n != 0) {
		data = n % 10;
		for (int i = data; i >= 1; i--) {
			if (k == 1) {
				str_data = i + '0';
				result = str_data + result;
			}
			else if (k == 2) {
				result = 'S' + result;
			}
			else if (k == 3) {
				result = 'B' + result;
			}			
		}
		n = n / 10;
		k++;
	}
	cout << result;
	return 0;
}

代码(柳神):

#include <iostream>
using namespace std;
int main() {
    int a, i = 0;
    cin >> a;
    int b[3] = {0};
    while (a != 0) {
        b[i++] = a % 10;
        a = a / 10;
    }
    for (int k = 0; k < b[2]; k++)
        cout << "B";
    for (int k = 0; k < b[1]; k++)
        cout << "S";
    for (int k = 0; k < b[0]; k++)
        cout << k + 1;
    return 0;
}

对比上下两种代码,下面的代码虽然用了三个for循环,但在时间复杂度上是没有增加的,且在输出时不申请额外的变量空间。我的代码虽然只有一个for循环体,但在中间用了三个if判断看起来并不是很美观。下面的代码申请了一个大小为三的数组去代替了上面代码的三个if判断。两个代码在执行效率上其实没有太大的差别,但是在美观和简洁程度上下面更胜一筹。


1007 素数对猜想 (20 分)

让我们定义dnd_n为:dn=pn+1pnd_n=p_{n+1}−p_n,其中pip_i是第i个素数。显然有d1d_1=1,且对于n>1有dnd_n是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。

现给定任意正整数N(<10510^5),请计算不超过N的满足猜想的素数对的个数。

输入格式:

输入在一行给出正整数N

输出格式:

在一行中输出不超过N的满足猜想的素数对的个数。

输入样例:

20
结尾无空行

输出样例:

4
结尾无空行

解析:

这题首先分析题目给的条件,就是要在特定范围内找出素数对的个数。那么这里我们首先要解决找素数的问题,然后再在找出来的素数中寻找素数对,统计个数。寻找素数对的方法很简单,只要在每次找到一个素数的时候,比较相邻前一个的素数,看差值是否为2。是则总数加一。

代码:

#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
bool is_prime(int x) {
	int val;
	val = sqrt(x);
	for (int i = 2; i <= val; i++) {
		if (x%i==0) return false;	
	}
	return true;
}
int main() {
	int n,sum=0,prime_num=0;
	cin >> n;
	vector<int> v(n);
	for (int i = 2; i <= n; i++) {
		if (is_prime(i)) {
			v[prime_num] = i;			
			if (prime_num > 0 && v[prime_num] - v[prime_num - 1] == 2) sum++;
			prime_num++;
		}
	}
	cout << sum;
}

1008 数组元素循环右移问题 (20 分)

一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1AN1A_0A_1⋯A_N−1)变换为(ANMAN1A0A1ANM1A_{N−M}⋯A_{N−1}A_0A_1⋯A_{N−M−1})(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?

输入格式:

每个输入包含一个测试用例,第1行输入N(1≤N≤100)和M(≥0);第2行输入N个整数,之间用空格分隔。

输出格式:

在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。

输入样例:

6 2
1 2 3 4 5 6
结尾无空行

输出样例:

5 6 1 2 3 4
结尾无空行

解析:

这道题就是输入一个数组,指定右移的格数然后输出,要求移动步数最少。其实如果我们真的去移动这个数组,重新申请一个数组空间去移动的话会非常的麻烦。这里,我们直接找到右移之后读取的开始位置,然后去顺序读取就可以了!移动步数为0!

代码:

#include <iostream>
#include <vector>
using namespace std;
int main() {
	int n, m,flag = 0,sign;
	cin >> n >> m;
	vector<int> v(n);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
	}
	int o = n - m % n;
	for (int i = o; i < o+n; i++) {
		if (flag == 1) cout << " ";
		sign = i % n;
		cout << v[sign];
		flag = 1;
	}	
}

1009 说反话 (20 分)

给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。

输入格式:

测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区分)组成的字符串,单词之间用 1 个空格分开,输入保证句子末尾没有多余的空格。

输出格式:

每个测试用例的输出占一行,输出倒序后的句子。

输入样例:

Hello World Here I Come

输出样例:

Come I Here World Hello

解析:

思路一:做字符串分割,然后倒叙输出。 思路二:利用栈先进后出的原则。

代码:

这里用思路二解决。注:这里要用if去判断一行是否输入完整。

#include <iostream>
#include <stack>
#include <string>
using namespace std;

int main() {
	string santence;	
	stack<string> data;
	while (cin >> santence) {
		data.push(santence);
		if (cin.get() == '\n')break;
	}
	cout << data.top();
	data.pop();
	while (!data.empty()) {
		cout << " " << data.top();
		data.pop();
	}
	return 0;
}

1010 一元多项式求导 (25 分)

设计函数求一元多项式的导数。(注:xnx^n(n为整数)的一阶导数为nxn1nx^{n−1}。)

输入格式:

以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过 1000 的整数)。数字间以空格分隔。

输出格式:

以与输入相同的格式输出导数多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。注意“零多项式”的指数和系数都是 0,但是表示为 0 0

输入样例:

3 4 -5 2 6 1 -2 0
结尾无空行

输出样例:

12 3 -10 1 6 0
结尾无空行

解析:

成对成对处理数据就好。

代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
	int n,m,flag=0;

	while (cin >> n>>m) {
		if (m != 0) {
			if (flag == 1)cout << " ";
			cout << n*m << " " << m - 1;
			flag = 1;
		}
		
		if (cin.get() == '\n')break;
	}
	if (flag == 0) cout << "0 0";

	
	return 0;
}

1011 A+B 和 C (15 分)

给定区间 [−231,231] 内的 3 个整数 A、B 和 C,请判断 A+B 是否大于 C。

输入格式:

输入第 1 行给出正整数 T (≤10),是测试用例的个数。随后给出 T 组测试用例,每组占一行,顺序给出 A、B 和 C。整数间以空格分隔。

输出格式:

对每组测试用例,在一行中输出 Case #X: true 如果 A+B>C,否则输出 Case #X: false,其中 X 是测试用例的编号(从 1 开始)。

输入样例:

4
1 2 3
2 3 4
2147483647 0 2147483646
0 -2147483648 -2147483647
结尾无空行

输出样例:

Case #1: false
Case #2: true
Case #3: true
Case #4: false
结尾无空行

解析:

大数相加,用C++的long long就能解决。int类型的范围是[231,2311][-2^{31},2^{31}-1],所以这里范围超出了int类型,用long long类型去解决。

代码:

#include <iostream>
using namespace std;
int main() {
	int n;
	cin >> n;
	long long a, b, c;
	for (int i = 0; i < n; i++) {
		cin >> a >> b >> c;
		if (a + b > c) {
			cout << "Case #"<<i+1<<": true" << endl;
		}
		else {
			cout << "Case #" << i + 1 << ": false" << endl;
		}
	}
}

1012 数字分类 (20 分)

给定一系列正整数,请按要求对数字进行分类,并输出以下 5 个数字:

  • A1A1 = 能被 5 整除的数字中所有偶数的和;
  • A2A2 = 将被 5 除后余 1 的数字按给出顺序进行交错求和,即计算 n1n2+n3n4n_1−n_2+n_3−n_4⋯
  • A3A3 = 被 5 除后余 2 的数字的个数;
  • A4A4 = 被 5 除后余 3 的数字的平均数,精确到小数点后 1 位;
  • A5A5 = 被 5 除后余 4 的数字中最大数字。

输入格式:

每个输入包含 1 个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N,随后给出 N 个不超过 1000 的待分类的正整数。数字间以空格分隔。

输出格式:

对给定的 N 个正整数,按题目要求计算 A1​~A5​ 并在一行中顺序输出。数字间以空格分隔,但行末不得有多余空格。

若其中某一类数字不存在,则在相应位置输出 N

输入样例 1:

13 1 2 3 4 5 6 7 8 9 10 20 16 18
结尾无空行

输出样例 1:

30 11 2 9.7 9
结尾无空行

输入样例 2:

8 1 2 4 5 6 7 9 16
结尾无空行

输出样例 2:

N 11 2 N 9
结尾无空行

解析:

数字分类其实就是根据运算结果的不同,做不同状态的处理,这里用可以选择用switch语句比较直观的去表现。也可以用数组的方式去实现。最后输出的时候要注意状态A2中如果处理过数据最后结果即使是0,那就要输出0,若没处理过数据就输出N。

代码:

#include <iostream>

using namespace std;
int main() {
	int n;
	
	while (cin >> n) {
		int left, a, a1 = 0, a2 = 0, a2_sign =0,a3 = 0, a5 = -1, flag = 0, sum_a4 = 0;
		double a4 = 0;
		for (int i = 0; i < n; i++) {
			cin >> a;
			left = a % 5;
			switch (left) {
			case 0:
				if (a % 2 == 0)a1 += a;
				break;
			case 1:
				a2_sign = 1;
				if (flag == 0) {
					a2 += a;
					flag = 1;
				}
				else {
					a2 -= a;
					flag = 0;
				}
				break;
			case 2:
				a3++;
				break;
			case 3:
				a4 += a;
				sum_a4++;
				break;
			case 4:
				if (a > a5)a5 = a;
				break;
			default:
				break;
			}

		}
		if (sum_a4 != 0)a4 = a4 / sum_a4;
		if (a1 != 0) {
			printf("%d ", a1);
		}
		else {
			printf("N ");
		}
		if (a2_sign ==1) {
			printf("%d ", a2);
		}
		else {
			printf("N ");
		}
		if (a3 != 0) {
			printf("%d ", a3);
		}
		else {
			printf("N ");
		}
		if (a4 != 0) {
			printf("%.1f ", a4);
		}
		else {
			printf("N ");
		}
		if (a5 == -1) {
			printf("N");
		}
		else {
			printf("%d", a5);
		}
	}
	
}

1013 数素数 (20 分)

令 PiP_i 表示第 i 个素数。现任给两个正整数 MN104M≤N≤10_4,请输出 PMP_M 到 PNP_N 的所有素数。

输入格式:

输入在一行中给出 M 和 N,其间以空格分隔。

输出格式:

输出从 PMP_M 到 PNP_N 的所有素数,每 10 个数字占 1 行,其间以空格分隔,但行末不得有多余空格。

输入样例:

5 27
结尾无空行

输出样例:

11 13 17 19 23 29 31 37 41 43
47 53 59 61 67 71 73 79 83 89
97 101 103
结尾无空行

1013 数素数 (20 分)

令 PiP_i 表示第 i 个素数。现任给两个正整数 M≤N≤104,请输出 PMP_M 到 PNP_N 的所有素数。

输入格式:

输入在一行中给出 M 和 N,其间以空格分隔。

输出格式:

输出从 PMP_M 到 PNP_N 的所有素数,每 10 个数字占 1 行,其间以空格分隔,但行末不得有多余空格。

输入样例:

5 27
结尾无空行

输出样例:

11 13 17 19 23 29 31 37 41 43
47 53 59 61 67 71 73 79 83 89
97 101 103
结尾无空行

解析:

这题其实就是素数输出,唯一注意的就是格式。

代码:

#include <iostream>
#include <math.h>
using namespace std;
bool is_Prime(int x) {
	int val;
	val = sqrt(x);
	for (int i = 2; i <= val; i++) {
		if (x%i == 0)return false;
	}
	return true;
}
int main() {
	int m, n,sum=0;
	cin >> m >> n;
	int x = 2;
	for (int i = 0; i < n;) {
		if (is_Prime(x)) {
			i++;
			if (i >= m) {
				sum = i - m + 1;
				if (sum % 10 != 1)cout << " ";
				cout << x;
				if(sum %10==0)cout<< endl;
				
			}
		}
		x++;
	}
	return 0;
}

1014 福尔摩斯的约会 (20 分)

大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm。大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间星期四 14:04,因为前面两字符串中第 1 对相同的大写英文字母(大小写有区分)是第 4 个字母 D,代表星期四;第 2 对相同的字符是 E ,那是第 5 个英文字母,代表一天里的第 14 个钟头(于是一天的 0 点到 23 点由数字 0 到 9、以及大写字母 A 到 N 表示);后面两字符串第 1 对相同的英文字母 s 出现在第 4 个位置(从 0 开始计数)上,代表第 4 分钟。现给定两对字符串,请帮助福尔摩斯解码得到约会的时间。

输入格式:

输入在 4 行中分别给出 4 个非空、不包含空格、且长度不超过 60 的字符串。

输出格式:

在一行中输出约会的时间,格式为 DAY HH:MM,其中 DAY 是某星期的 3 字符缩写,即 MON 表示星期一,TUE 表示星期二,WED 表示星期三,THU 表示星期四,FRI 表示星期五,SAT 表示星期六,SUN 表示星期日。题目输入保证每个测试存在唯一解。

输入样例:

3485djDkxh4hhGE 
2984akDfkkkkggEdsb 
s&hgsfdk 
d&Hyscvnm
结尾无空行

输出样例:

THU 14:04
结尾无空行

解析:

这道题我个人认为描述的不是很清晰。首先在前两个字符串中寻找相同的且必须是大写的字母,也就是范围应当在A-G,分别代表了周一到周日。然后应当在寻找到第一个相同的之后继续寻找下一个而不是重新开始,且范围应该是0-9、A-N之间(一共24个字符代表24小时),找完两个之后,寻找另外两个字符串,代表分钟,即从0开始计数,相应的下标就是分钟数。 这里我们可以用字符串数组去当字典表记录周一到周日所代表的相应字符。且在比较的时候比较长度应该以两者中最小的那个字符串长度为准。最后注意格式输出即可。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	int flag_day = 0;
	string str1, str2, str3, str4,str_empty;
	string arrayDay[7] = {"MON","TUE","WED","THU","FRI","SAT","SUN"};
	
	cin >> str1 >> str2 >> str3 >> str4;
	if (str1.size() > str2.size()) {
		str_empty = str1;
		str1 = str2;
		str2 = str_empty;
	}
	if (str3.size() > str4.size()) {
		str_empty = str3;
		str3 = str4;
		str4 = str_empty;
	}
	for (int i = 0; i < str1.size(); i++) {
		if (flag_day == 0 && str1[i]>='A' && str1[i]<='G' && str1[i] == str2[i]) {
			int pos = str1[i] - 'A';
			cout << arrayDay[pos] <<" ";
			flag_day = 1;
		}else if (flag_day == 1 && str1[i] == str2[i]) {
			if (str1[i] >= '0'&&str1[i] <= '9') {
				cout << "0" << str1[i] << ":";
				break;
			}
			if (str1[i] >= 'A'&&str1[i] <= 'N') {
				cout << str1[i] - '7' << ":";
				break;
			}
		}
	}
	for (int i = 0; i < str3.size(); i++) {
		if (str3[i] == str4[i] && (str3[i] >= 'A'&&str3[i]<='Z'|| str3[i] >= 'a'&&str3[i] <= 'z')) {
			if (i >= 0 && i <= 9) {
				cout << "0" << i;
				break;
			}
			else {
				cout << i;
				break;
			}
		}
		
	}
}

1015 德才论 (25 分)

宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人。凡取人之术,苟不得圣人,君子而与之,与其得小人,不若得愚人。”

现给出一批考生的德才分数,请根据司马光的理论给出录取排名。

输入格式:

输入第一行给出 3 个正整数,分别为:N(≤105),即考生总数;L(≥60),为录取最低分数线,即德分和才分均不低于 L 的考生才有资格被考虑录取;H(<100),为优先录取线——德分和才分均不低于此线的被定义为“才德全尽”,此类考生按德才总分从高到低排序;才分不到但德分到线的一类考生属于“德胜才”,也按总分排序,但排在第一类考生之后;德才分均低于 H,但是德分不低于才分的考生属于“才德兼亡”但尚有“德胜才”者,按总分排序,但排在第二类考生之后;其他达到最低线 L 的考生也按总分排序,但排在第三类考生之后。

随后 N 行,每行给出一位考生的信息,包括:准考证号 德分 才分,其中准考证号为 8 位整数,德才分为区间 [0, 100] 内的整数。数字间以空格分隔。

输出格式:

输出第一行首先给出达到最低分数线的考生人数 M,随后 M 行,每行按照输入格式输出一位考生的信息,考生按输入中说明的规则从高到低排序。当某类考生中有多人总分相同时,按其德分降序排列;若德分也并列,则按准考证号的升序输出。

输入样例:

14 60 80
10000001 64 90
10000002 90 60
10000011 85 80
10000003 85 80
10000004 80 85
10000005 82 77
10000006 83 76
10000007 90 78
10000008 75 79
10000009 59 90
10000010 88 45
10000012 80 100
10000013 90 99
10000014 66 60
结尾无空行

输出样例:

12
10000013 90 99
10000012 80 100
10000003 85 80
10000011 85 80
10000004 80 85
10000007 90 78
10000006 83 76
10000005 82 77
10000002 90 60
10000014 66 60
10000008 75 79
10000001 64 90
结尾无空行

解析:

这道题比较模式非常的复杂,有四种类型。

  1. 德才均大于等于H
  2. 德大于H,但才大于等于L却不高于H
  3. 德才均小于H,但大于L,且德大于才
  4. 德才大于等于L 且有排序三个条件:
  5. 按总分排序
  6. 若总分相等按德分降序排列
  7. 若德分也相等,则按准考证号的升序输出 有三个元素,学号,德分,才分。这三个元素第一反应是三元组,但C++设计三元组会相对麻烦,这里我们可以设计结构体struct。如果用数组去设计,则三个元素会被分离开,不方便排序和比较。这里参考了柳神的代码。下面会PO出。

这里会用到一个很重要的函数叫sort函数,在引用包应为: #include <algorithm>,并且这里用vector和struct结构体设计了一个二维数组。

sort函数解析
元素类型接受使用>或<运算符 只能对vector,array,deque这三个容器进行排序
在不适用cmp(比较规则)的情况下,默认使用升序。
时间复杂度为nlog2(n)nlog_2(n)

#include \<algorithm>
sort(first,last,比较规则)

bool compare(int a,int b)
{   
return a>b;//return b>a;从小到大
}
int main()
{
    int a[10]={9,6,3,8,5,2,7,4,1,0};
    for(int i=0;i<10;i++)
        cout<<a[i]<<endl;  
    sort(a,a+10,compare);//在这里就不需要对compare函数传入参数了 for(int i=0;i<10;i++)
        cout<<a[i]<<endl;
    return 0;
}

sort原理 sort()主要由两种方法组成:插入排序和快速排序。C++中的STL(standard Template Library)标准模板库(一些容器的集合如list map vector等)中SORT_MAX变量来进行判断,如果大于SORT_MAX就使用快排,否则使用插排。

代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//设计结构体
struct student {
	int code, de, cai;
};
int cmp(struct student a, struct student b) {
	if ((a.de + a.cai) != (b.de + b.cai)) {
		return (a.de + a.cai) > (b.de + b.cai);
	}
	else if (a.de != b.de) {
		return a.de > b.de;
	}
	else {
		return a.code < b.code;
	}
}

int main() {
	int  n, low, high;
	cin >> n >> low >> high;
	vector<student> v[4];//因为一共有四类需要存放
	student temp;
	int total = n;
	for (int i = 0; i < n; i++) {
		cin >> temp.code >> temp.de >> temp.cai;
		if (temp.de < low || temp.cai < low) {
			total--;
		}
		else if (temp.de >= high && temp.cai >= high) {
			v[0].push_back(temp);
		}
		else if (temp.de >= high && temp.cai < high) {
			v[1].push_back(temp);
		}
		else if (temp.de < high && temp.cai < high && temp.de >= temp.cai) {
			v[2].push_back(temp);
		}
		else {
			v[3].push_back(temp);
		}
	}
	cout << total<<endl;
	for (int i = 0; i < 4; i++) {
		sort(v[i].begin(), v[i].end(), cmp);
		for (int j = 0; j < v[i].size(); j++) {
			cout << v[i][j].code<<" " << v[i][j].de << " "<<v[i][j].cai<<endl;
		}
	}
	return 0;
}

1016 部分A+B (15 分)

正整数 A 的“DAD_A(为 1 位整数)部分”定义为由 A 中所有 DAD_A 组成的新整数DAD_A。例如:给定 A=3862767,DAD_A=6,则 A 的“6 部分”PAP_A 是 66,因为 A 中有 2 个 6。

现给定 A、DAD_A、B、DbD_b,请编写程序计算 PA+PBP_A+P_B

输入格式:

输入在一行中依次给出 A、DAD_A、B、DBD_B,中间以空格分隔,其中 0<A,B<10910^9

输出格式:

在一行中输出 PA+PBP_A+P_B 的值。

输入样例 1:

3862767 6 13530293 3

输出样例 1:

399

输入样例 2:

3862767 1 13530293 8

输出样例 2:

0

解析:

这题就是字符串的分析。以及字符串和整型之间的转换。

int atoi(const char *nptr)函数表示ascii to integer即ascii字符转换成整型的一个函数

str.c_str()函数,因为在C语言中没有string类型,所以必须通过string类对象的成员函数c_str()把string对象转换成c中的字符串样式。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	string a, b,pa,pb;
	int da, db;
	cin >> a >> da >> b >> db;
	for (int i = 0; i < a.size(); i++) {
		if ((a[i] - '0') == da)pa += a[i];
	}
	for (int i = 0; i < b.size(); i++) {
		if ((b[i] - '0') == db)pb += b[i];
	}
	int all = atoi(pa.c_str())+ atoi(pb.c_str());
	cout << all;
}

1017 A除以B (20 分)

本题要求计算 A/B,其中 A 是不超过 1000 位的正整数,B 是 1 位正整数。你需要输出商数 Q 和余数 R,使得 A=B×Q+R 成立。

输入格式:

输入在一行中依次给出 A 和 B,中间以 1 空格分隔。

输出格式:

在一行中依次输出 Q 和 R,中间以 1 空格分隔。

输入样例:

123456789050987654321 7
结尾无空行

输出样例:

17636684150141093474 3
结尾无空行

解析:

这题看到1000位的数,就知道属于大数类。但c++中没有符合的类型,因此用string类去模拟除法的运算。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {	
		string s, q;
		int a, b, r = 0, flag = 0;
		cin >> s >> b;
		for (int i = 0; i < s.size(); i++) {
			a = r + (s[i] - '0');
			r = a % b;
			q += (a / b) + '0';
			if (i != s.size() - 1) {
				r = r * 10;
			}
		}
		for (int i = 0; i < q.size(); i++) {
			if (q.size() == 1 && q[0] == '0') {
				cout << q[0];
				break;
			}
			if (q[i] != '0'&&flag == 0) {
				flag = 1;
				cout << q[i];
			}
			else if(flag == 1){
				cout << q[i];
			}
		}
		cout << " " << r<<endl;	
}

1018 锤子剪刀布 (20 分)

大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示:

image.png
现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。

输入格式:

输入第 1 行给出正整数 N(≤10510_5),即双方交锋的次数。随后 N 行,每行给出一次交锋的信息,即甲、乙双方同时给出的的手势。C 代表“锤子”、J 代表“剪刀”、B 代表“布”,第 1 个字母代表甲方,第 2 个代表乙方,中间有 1 个空格。

输出格式:

输出第 1、2 行分别给出甲、乙的胜、平、负次数,数字间以 1 个空格分隔。第 3 行给出两个字母,分别代表甲、乙获胜次数最多的手势,中间有 1 个空格。如果解不唯一,则输出按字母序最小的解。

输入样例:

10
C J
J B
C B
B B
B C
C C
C B
J B
B C
J J
结尾无空行

输出样例:

5 3 2
2 3 5
B B
结尾无空行

解析:

这题其实就是排列组合,针对几种情况的操作。没有什么太大的难度,只是在对几种情况的分类会有一些灵活的处理,这里我采用二维数组的方式进行操作。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	int n,win=0,peace=0,lose=0, index_i, index_j, max_jia = -1, max_yi = -1;
	cin >> n;
	string jia, yi,s="BCJ",str_max_jia,str_max_yi;
	int array[4][4] = { {0,-1,1,0},{1,0,-1,0},{-1,1,0,0},{0,0,0,0} };
	for (int i = 0; i < n; i++) {
		cin >> jia >> yi;
		index_i = s.find(jia);
		index_j = s.find(yi);
		if (array[index_i][index_j] == 0) {
			peace++;
		}
		else if(array[index_i][index_j] == -1){
			win++;
			array[index_i][3]++;
		}
		else {
			lose++;
			array[3][index_j]++;
		}
	}
	for (int i = 0; i < 4; i++) {
		if (array[i][3] > max_jia) {
			max_jia = array[i][3];
			str_max_jia = s[i];
		}
		if (array[3][i] > max_yi) {
			max_yi = array[3][i];
			str_max_yi = s[i];
		}
	}
	cout << win << " " << peace << " " << lose << endl;
	cout << lose << " " << peace << " " << win << endl;
	cout << str_max_jia << " " << str_max_yi;
}

1019 数字黑洞 (20 分)

给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字。一直重复这样做,我们很快会停在有“数字黑洞”之称的 6174,这个神奇的数字也叫 Kaprekar 常数。

例如,我们从6767开始,将得到

7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174
7641 - 1467 = 6174
... ...

现给定任意 4 位正整数,请编写程序演示到达黑洞的过程。

输入格式:

输入给出一个 (0,10410_4) 区间内的正整数 N。

输出格式:

如果 N 的 4 位数字全相等,则在一行内输出 N - N = 0000;否则将计算的每一步在一行内输出,直到 6174 作为差出现,输出格式见样例。注意每个数字按 4 位数格式输出。

输入样例 1:

6767
结尾无空行

输出样例 1:

7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174
结尾无空行

输入样例 2:

2222
结尾无空行

输出样例 2:

2222 - 2222 = 0000
结尾无空行

解析:

害这道题,可以说是相当巧妙了。实际上就是排序问题。之前认为sort是针对容器的排序,没想到,这里可以直接对string类进行排序。难道c++中的字符串类本质就是一个容器吗。其实string类在底层实际上是一个char[]数组,所以,我们这里可以直接用sort()进行排序。
最后还要注意输出格式,这里非常巧妙的用了字符串的insert操作,使每个数的格式始终保持在四位,不够的位数会用0代替。\

TIPS
这里使用do..while结构而不是while结构是因为假如输入值为6174时,while循环就会进不去,第一遍也就不执行。这里我们需要第一遍执行,所以用do..while结构才是正确的选择。

代码:

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
bool compare(int a, int b){	return a > b;};
int main() {
	int result = 0;
	string n;
	cin >> n;
	n.insert(0, 4 - n.length(), '0');
	do{
		string a = n, b = n;
		sort(a.begin(), a.end(), compare);
		sort(b.begin(), b.end());
		result = (atoi(a.c_str()) - atoi(b.c_str()));
		n = to_string(result);
		n.insert(0, 4 - n.length(), '0');		
		cout << a << " - " << b << " = " << n << endl;
	} while (n != "6174" && n != "0000");
	return 0;
}

1020 月饼 (25 分)

月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。

注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。

输入格式:

每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。

输出格式:

对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。

输入样例:

3 20
18 15 10
75 72 45
结尾无空行

输出样例:

94.50
结尾无空行

解析:

其实这道题的营销策略就是按总售价/库存的值从高到低售卖即可。这里有一个技术点,就是关于多条信息绑定排序,那么一般遇到这样的问题,我们会采用结构体绑定来进行整体的比较,使其中某项信息在比较时重新排序后其他信息不会丢失。

代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include<iomanip>
using namespace std;
struct mooncake { float reserve, totalprice, ratio; };
bool compare(mooncake a, mooncake b) { return a.ratio > b.ratio; };
int main() {
	int n, market;
	float result = 0;
	cin >> n>>market;
	vector<mooncake> v(n);
	for (int i = 0; i < n; i++) cin >> v[i].reserve;
	for (int i = 0; i < n; i++) {
		cin >> v[i].totalprice;
		v[i].ratio = v[i].totalprice / v[i].reserve;
	}
	sort(v.begin(), v.end(), compare);
	for (int i = 0; i < n; i++) {
		if (v[i].reserve <= market) {
			result += v[i].totalprice;
		}
		else {
			result += v[i].ratio*market;
			break;
		}
		market = market - v[i].reserve;
	}
	cout << fixed << setprecision(2) << result << endl;

}

1021 个位数统计 (15 分)

给定一个 k 位整数 N=dk110k1++d1101+d0 (0di9, i=0,,k1, dk1>0)N=d_{k−1}10^{k−1}+⋯+d_110^1+d_0 (0≤d_i≤9, i=0,⋯,k−1, d_k−1>0),请编写程序统计每种不同的个位数字出现的次数。例如:给定 N=100311,则有 2 个 0,3 个 1,和 1 个 3。

输入格式:

每个输入包含 1 个测试用例,即一个不超过 1000 位的正整数 N。

输出格式:

对 N 中每一种不同的个位数字,以 D:M 的格式在一行中输出该位数字 D 及其在 N 中出现的次数 M。要求按 D 的升序输出。

输入样例:

100311
结尾无空行

输出样例:

0:2
1:3
3:1
结尾无空行

解析:

这题就是遍历字符串,对出现过的数的统计。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s;
	cin >> s;
	int array[10] = { 0 };
	for (int i = 0; i < s.size(); i++) {
		int pos = s[i] - '0';
		array[pos]++;
	}
	for (int i = 0; i < 10; i++) {
		if (array[i] != 0)cout << i << ":" << array[i] << endl;
	}
}

1022 D进制的A+B (20 分)

输入两个非负 10 进制整数 A 和 B (≤23012^30−1),输出 A+B 的 D (1<D≤10)进制数。

输入格式:

输入在一行中依次给出 3 个整数 A、B 和 D。

输出格式:

输出 A+B 的 D 进制数。

输入样例:

123 456 8

输出样例:

1103

解析:

这道题利用十进制转换成二进制的除二取余法的思想就可以了。

代码:

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
	int a, b, d, c, reserve;
	string result;
	cin >> a >> b >> d;
	c = a + b;	
	if (d == 10) {
		cout << c;
	}
	else {
		do {
			reserve = c % d;
			c = c / d;
			result = to_string(reserve) + result;
		} while (c != 0);
	}
	cout << result;
}

1023 组个最小数 (20 分)

给定数字 0-9 各若干个。你可以以任意顺序排列这些数字,但必须全部使用。目标是使得最后得到的数尽可能小(注意 0 不能做首位)。例如:给定两个 0,两个 1,三个 5,一个 8,我们得到的最小的数就是 10015558。

现给定数字,请编写程序输出能够组成的最小的数。

输入格式:

输入在一行中给出 10 个非负整数,顺序表示我们拥有数字 0、数字 1、……数字 9 的个数。整数间用一个空格分隔。10 个数字的总个数不超过 50,且至少拥有 1 个非 0 的数字。

输出格式:

在一行中输出能够组成的最小的数。

输入样例:

2 2 0 0 0 3 0 0 1 0
结尾无空行

输出样例:

10015558
结尾无空行

解析:

要输出最小的数,且第一位不能是0。所以这里我们的解决方案是,首先将输入的数存在数组里。然后对第一个要输出的数从1开始遍历,直到不为0。然后再对第一个之后的数输出,则从数组的0开始遍历。

代码:

#include <iostream>
using namespace std;
int main() {
	int array[10], pos = 0;
	for (int i = 0; i < 10; i++)cin >> array[i];
	for (int i = 1; i < 10; i++) {
		if (array[i] != 0) {
			cout << i;
			array[i]--;
			break;
		}
	}
	for (int i = 0; i < 10; i++) {
		while (array[i] != 0) {
			cout << i;
			array[i]--;
		}
	}
}

1024 科学计数法 (20 分)

科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式 [+-][1-9].[0-9]+E[+-][0-9]+,即数字的整数部分只有 1 位,小数部分至少有 1 位,该数字及其指数部分的正负号即使对正数也必定明确给出。

现以科学计数法的格式给出实数 A,请编写程序按普通数字表示法输出 A,并保证所有有效位都被保留。

输入格式:

每个输入包含 1 个测试用例,即一个以科学计数法表示的实数 A。该数字的存储长度不超过 9999 字节,且其指数的绝对值不超过 9999。

输出格式:

对每个测试用例,在一行中按普通数字表示法输出 A,并保证所有有效位都被保留,包括末尾的 0。

输入样例 1:

+1.23400E-03
结尾无空行

输出样例 1:

0.00123400
结尾无空行

输入样例 2:

-1.2E+10
结尾无空行

输出样例 2:

-12000000000
结尾无空行

解析:

这题真的是对字符串的各种操作的综合考验啊。非常的灵活。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
		int dot_pos;
		string s, result;
		cin >> s;
		if (s[0] == '-')cout << '-';
		int e_index = s.find_first_of('E');
		int signal_index = e_index + 1;
		string str_exponent(s, signal_index + 1);
		int exponent = atoi(str_exponent.c_str());
		result = s.substr(1, e_index - 1);
		int result_dot_index = result.find_first_of('.');
		if (exponent == 0) {
			cout << result;
			return 0;
		}
		if (s[signal_index] == '-') {
			
			dot_pos = 1;
			result.erase(result_dot_index, 1);
			result.insert(0, exponent, '0');
			result.insert(dot_pos, 1, '.');
			cout << result;
			return 0;
		}
		else {
			int  i = result.size() - result_dot_index - 1;
			if (i <= exponent) {				
				result.insert(result.size(), exponent - i, '0');
				result.erase(result_dot_index, 1);
				cout << result;
				return 0;
			}
			else {
				dot_pos = result_dot_index + exponent;
				result.erase(result_dot_index, 1);
				result.insert(dot_pos, 1, '.');
				cout << result;
				return 0;
			}
		}
}

1025 反转链表 (25 分)

给定一个常数 K 以及一个单链表 L,请编写程序将 L 中每 K 个结点反转。例如:给定 L 为 1→2→3→4→5→6,K 为 3,则输出应该为 3→2→1→6→5→4;如果 K 为 4,则输出应该为 4→3→2→1→5→6,即最后不到 K 个元素不反转。

输入格式:

每个输入包含 1 个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 N (≤105)、以及正整数 K (≤N),即要求反转的子链结点的个数。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。

接下来有 N 行,每行格式为:

Address Data Next

其中 Address 是结点地址,Data 是该结点保存的整数数据,Next 是下一结点的地址。

输出格式:

对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
结尾无空行

输出样例:

00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
结尾无空行

解析:

这道题可以说是相当好且综合的题目了,考到了链表的反转。那么我们首先要构建链表,对链表进行排序,最后反转输出即可。其实前面的几步都比较简单,但是在最后的反转输出上我花了很多功夫,主要是next地址的转换与判断,以及最后一个-1的输出上。最后一步反转输出可以优化。有一说一,成堆的if...else是一个很low的写法。我们将尝试对它进行优化输出。我们将会用到一个函数reverse()反转,该函数所对应的头文件为#inclued <algorithm>

代码(未优化)[60行]:

#include <iostream>
#include <algorithm>
using namespace std;
struct Node {//1.定义静态链表
	int next, data;
	int address = -1;
	int	order = 100001;//2.节点初始化
}node[100001];//创建一个大小是100001的节点数组
bool cmp(Node a, Node b) { return a.order < b.order; };
int main() {
	int init_address, n, k, address;
	scanf("%d%d%d", &init_address, &n, &k);
	for (int i = 0; i < n; i++) {
		scanf("%d", &address);
		scanf("%d%d", &node[address].data, &node[address].next);
		node[address].address = address;
	}
	int p = init_address, count = 0;
	while (p != -1) {//3.遍历链表找出单链表的所有有效节点
		node[p].order = count++;
		p = node[p].next;
	}
	sort(node, node + 100001,cmp);//4.对链表进行排序
	//下面就是按题目输出链表
	int times = count / k, sk = k, pos ,reserve = count % k;
	if (times == 0) {
		for (int i = 0; i < count - 1; i++) printf("%05d %d %05d\n", node[i].address, node[i].data, node[i].next);
		printf("%05d %d -1", node[count - 1].address, node[count - 1].data, node[count - 1].next);
		return 0;
	}
	for (int i = 1; i <= times; i++) {
		sk = k;
		pos = sk * i - 1;
		while (sk != 0) {
			if (sk == 1) {
				if (k * i + k - 1 < count) {
					node[pos].next = node[k * i+k-1].address;
				}
				else {
					node[pos].next = node[k * i].address;
				}				
			}
			else {
				node[pos].next = node[pos - 1].address;				
			}
			if (node[pos].next != -1) {
				printf("%05d %d %05d\n", node[pos].address, node[pos].data, node[pos].next);
			}
			else {
				printf("%05d %d -1", node[pos].address, node[pos].data);
			}
			pos--;
			sk--;
		}
	}
	for (int i = times * k; i < count-1; i++) {
		printf("%05d %d %05d\n", node[i].address, node[i].data, node[i].next);
	}
	if(reserve != 0)printf("%05d %d -1", node[count - 1].address, node[count - 1].data);
}

代码(优化)[30行]:

#include <iostream>
#include <algorithm>
using namespace std;
struct Node {//1.定义静态链表
	int next, data , address;
	int	order = 100001;//2.节点初始化
}node[100001];//创建一个大小是100001的节点数组
bool cmp(Node a, Node b) { return a.order < b.order; };
int main() {
	int init_address, n, k, address;
	scanf("%d%d%d", &init_address, &n, &k);
	for (int i = 0; i < n; i++) {
		scanf("%d", &address);
		scanf("%d%d", &node[address].data, &node[address].next);
		node[address].address = address;
	}
	int p = init_address, count = 0;
	while (p != -1) {//3.遍历链表找出单链表的所有有效节点
		node[p].order = count++;
		p = node[p].next;
	}
	sort(node, node + 100001,cmp);//4.对链表进行排序
	//下面就是按题目输出链表
	for (int i = 0; i < (count - count % k); i += k) reverse(begin(node) + i, begin(node) + i + k);
	for (int i = 0; i < count - 1; i++) {
		node[i].next = node[i + 1].address;
		printf("%05d %d %05d\n", node[i].address, node[i].data, node[i].next);
	}
	printf("%05d %d -1", node[count - 1].address, node[count - 1].data);
}

看到没有兄弟们!优化后的代码整整缩小了30行!reverse()函数实在是太强大啦有没有!简直舒适到要达到颅内高潮了!最后一步的输出代码由原来的几十行缩减到5行就完美解决。不过我这里使用结构体的方法是比较普适的,其中还用到了sort排序函数,实际上还有针对这道题目更加巧妙的解法。下面附上柳神的代码,真是一山更比一山高。

代码(柳神)[23行]:

#include <iostream>
#include <algorithm>
using namespace std;
int main() {
    int first, k, n, temp;
    cin >> first >> n >> k;
    int data[100005], next[100005], list[100005];
    for (int i = 0; i < n; i++) {
        cin >> temp;
        cin >> data[temp] >> next[temp];
    }
    int sum = 0;//不一定所有的输入的结点都是有用的,加个计数器
    while (first != -1) {
        list[sum++] = first;
        first = next[first];
    }
    for (int i = 0; i < (sum - sum % k); i += k)
        reverse(begin(list) + i, begin(list) + i + k);
    for (int i = 0; i < sum - 1; i++)
        printf("%05d %d %05d\n", list[i], data[list[i]], list[i + 1]);
    printf("%05d %d -1", list[sum - 1], data[list[sum - 1]]);
    return 0;
}

1026 程序运行时间 (15 分)

要获得一个 C 语言程序的运行时间,常用的方法是调用头文件 time.h,其中提供了 clock() 函数,可以捕捉从程序开始运行到 clock() 被调用时所耗费的时间。这个时间单位是 clock tick,即“时钟打点”。同时还有一个常数 CLK_TCK,给出了机器时钟每秒所走的时钟打点数。于是为了获得一个函数 f 的运行时间,我们只要在调用 f 之前先调用 clock(),获得一个时钟打点数 C1;在 f 执行完成后再调用 clock(),获得另一个时钟打点数 C2;两次获得的时钟打点数之差 (C2-C1) 就是 f 运行所消耗的时钟打点数,再除以常数 CLK_TCK,就得到了以秒为单位的运行时间。

这里不妨简单假设常数 CLK_TCK 为 100。现给定被测函数前后两次获得的时钟打点数,请你给出被测函数运行的时间。

输入格式:

输入在一行中顺序给出 2 个整数 C1 和 C2。注意两次获得的时钟打点数肯定不相同,即 C1 < C2,并且取值在 [0,107]。

输出格式:

在一行中输出被测函数运行的时间。运行时间必须按照 hh:mm:ss(即2位的 时:分:秒)格式输出;不足 1 秒的时间四舍五入到秒。

输入样例:

123 4577973
结尾无空行

输出样例:

12:42:59

解析:

这就是一类简单模拟的题,只要对两个输入的数的差值做相应的运算就可以了。

代码:

#include <iostream>
int main() {
	int c1, c2,c;
	scanf("%d%d", &c1, &c2);
	c = c2 - c1;
	if (c % 100 >= 50) {
		c = c / 100 + 1;
	}
	else {
		c = c / 100;
	}
	printf("%02d:%02d:%02d", c / 3600, c % 3600 / 60, c % 60);
	return 0;
}

1027 打印沙漏 (20 分)

本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

*****
 ***
  *
 ***
*****

所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。

给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。

输入格式:

输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。

输出格式:

首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。

输入样例:

19 *
结尾无空行

输出样例:

*****
 ***
  *
 ***
*****
2
结尾无空行

解析:

害,这道题也是考图形输出,也没啥吧,仔细点儿就好。

代码:

#include <iostream>
using namespace std;
int find_biggest(int x,int a) {
	x -= a;
	a += 2;
	if (x > 0) {
		return find_biggest(x, a);
	}
	else {
		return a-2-2;
	}
}
int main() {
	int n;
	char c;
	cin >> n >> c;
	int half = (n - 1) / 2, numn = 0, numc = 0 ,k = 0,t = 0;
	numc = find_biggest(half, 3);
	while (numc >= 1){
		t = numc;
		k = numn;
		while (k--)printf("%c", ' ');
		while (t--)printf("%c", c);
		printf("\n");
		numn++;
		n -= numc;
		numc -= 2;
	 } 
	numc += 4;
	numn -= 2;
	 while (numn >= 0) {
		 t = numc;
		 k = numn;
		 while (k--)printf("%c", ' ');
		 while (t--)printf("%c", c);
		 printf("\n");
		 numn--;
		 n -= numc;
		 numc += 2;
	 }
	 cout << n;
	return 0;
}

1028 人口普查 (20 分)

某城镇进行人口普查,得到了全体居民的生日。现请你写个程序,找出镇上最年长和最年轻的人。

这里确保每个输入的日期都是合法的,但不一定是合理的——假设已知镇上没有超过 200 岁的老人,而今天是 2014 年 9 月 6 日,所以超过 200 岁的生日和未出生的生日都是不合理的,应该被过滤掉。

输入格式:

输入在第一行给出正整数 N,取值在(0,105];随后 N 行,每行给出 1 个人的姓名(由不超过 5 个英文字母组成的字符串)、以及按 yyyy/mm/dd(即年/月/日)格式给出的生日。题目保证最年长和最年轻的人没有并列。

输出格式:

在一行中顺序输出有效生日的个数、最年长人和最年轻人的姓名,其间以空格分隔。

输入样例:

5
John 2001/05/12
Tom 1814/09/06
Ann 2121/01/30
James 1814/09/05
Steve 1967/11/20
结尾无空行

输出样例:

3 Tom John
结尾无空行

解析:

这道题!由于年份输入都是合法的,所以直接做字符串比较就可以了!!是不是很方便,其实也是一个元素查找的题目,要注意当合法数据输出为0的时候的格式嗷。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	int n,sum=0;
	scanf("%d", &n);
	string name, birth, oldest, youngest , maxbirth = "1814/09/06", minbirth = "2014/09/06";
	while (n--) {
		cin >> name >> birth;
		if (birth >= "1814/09/06" && birth <= "2014/09/06") {
			sum++;
			if (birth >= maxbirth) {
				maxbirth = birth;
				youngest = name;
			}
			if (birth <= minbirth) {
				minbirth = birth;
				oldest = name;
			}
		}
	}
	cout << sum;
	if(sum!=0)cout << " "<< oldest<< " " << youngest;
	return 0;
}

1029 旧键盘 (20 分)

旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现。现在给出应该输入的一段文字、以及实际被输入的文字,请你列出肯定坏掉的那些键。

输入格式:

输入在 2 行中分别给出应该输入的文字、以及实际被输入的文字。每段文字是不超过 80 个字符的串,由字母 A-Z(包括大、小写)、数字 0-9、以及下划线 _(代表空格)组成。题目保证 2 个字符串均非空。

输出格式:

按照发现顺序,在一行中输出坏掉的键。其中英文字母只输出大写,每个坏键只输出一次。题目保证至少有 1 个坏键。

输入样例:

7_This_is_a_test
_hs_s_a_es
结尾无空行

输出样例:

7TI
结尾无空行

解析:

这道题呜呜呜我觉得就是字符串处理,用find()函数和字符串处理类#include <cctype>里的toupper()函数处理这道题目不要太简单!所以掌握字符串处理的一些函数和这个类之后做题目不要太方便!我之前用for循环用KMP算法去写的时候直接超时。

代码:

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
	string in, out, result;
	cin >> in >> out;
	for (int i = 0; i < in.size(); i++)
		if (out.find(in[i]) == string::npos && result.find(toupper(in[i])) == string::npos)
			result += toupper(in[i]);
	cout << result;
	return 0;
}

1030 完美数列 (25 分)

给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。

现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。

输入格式:

输入第一行给出两个正整数 N 和 p,其中 N(≤105)是输入的正整数的个数,p(≤109)是给定的参数。第二行给出 N 个正整数,每个数不超过 109。

输出格式:

在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。

输入样例:

10 8
2 3 20 4 5 1 6 7 8 9
结尾无空行

输出样例:

8
结尾无空行

解析:

害这题其实就是找一个数,本质上考的是查找,我们这里可以使用upper_bound()函数实现一个二分查找。

注意upper_bound()返回的是一个指针地址,所以我们要在最后减去a

代码:

#include <iostream>
#include <algorithm>
using namespace std;
bool cmp(int a, int b) { return a > b; };
int main() {
	int n, p, a[100001] = { 0 };
	scanf("%d%d", &n, &p);
	for (int i = 0; i < n; i++)scanf("%d", &a[i]);
	sort(a, a + n);
	int ans = 1;
	for (int i = 0; i < n; i++ ) {
		int j = upper_bound(a + i + 1, a + n, (long long)a[i] * p) - a;
		ans = max(ans, j - i);
	}
	printf("%d", ans);
	return 0;
}

1031 查验身份证 (15 分)

一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:

首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:

Z:0 1 2 3 4 5 6 7 8 9 10
M:1 0 X 9 8 7 6 5 4 3 2

现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。

输入格式:

输入第一行给出正整数N(≤100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。

输出格式:

按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出All passed

输入样例1:

4
320124198808240056
12010X198901011234
110108196711301866
37070419881216001X
结尾无空行

输出样例1:

12010X198901011234
110108196711301866
37070419881216001X
结尾无空行

输入样例2:

2
320124198808240056
110108196711301862

输出样例2:

All passed

解析:

这题属于字符串处理,只要针对不同位数的字符串做判断和处理就可以了。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	int n, z = 0, sum = 0, h = 0, array[17] = { 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 };
	char m[11] = { '1','0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
	scanf("%d", &n);
	string s;
	while (n--) {
		cin >> s;
		h = 0;
		z = 0;
		for (int i = 0; i < 17; i++) {
			if (s[i]<'0' || s[i]>'9') {
				h = 1;
				sum = 1;
				cout << s <<'\n';
				break;
			}
			z += (s[i] - '0')*array[i];
		}
		if (h!=1&&m[z % 11] != s[17]) {
			sum = 1;
			cout << s << '\n';
		}
	}
	if(sum == 0)printf("All passed");
}

1032 挖掘机技术哪家强 (20 分)

为了用事实说明挖掘机技术到底哪家强,PAT 组织了一场挖掘机技能大赛。现请你根据比赛结果统计出技术最强的那个学校。

输入格式:

输入在第 1 行给出不超过 10510^5 的正整数 N,即参赛人数。随后 N 行,每行给出一位参赛者的信息和成绩,包括其所代表的学校的编号(从 1 开始连续编号)、及其比赛成绩(百分制),中间以空格分隔。

输出格式:

在一行中给出总得分最高的学校的编号、及其总分,中间以空格分隔。题目保证答案唯一,没有并列。

输入样例:

6
3 65
2 80
1 100
2 70
3 40
3 0

输出样例:

2 150

解析:

这道题也是元素查找题,就是对元素规整之后找最大值。唯一需要注意的是边界值问题。

代码:

#include <iostream>
#include <vector>
using namespace std;
bool cmp(int a, int b) { return a > b; };
int main() {
	int n;
	scanf("%d", &n);
	vector<int> v(n+1,0);
	int code, source,maxcode = -1,maxsource = 0;
	while (n--) {
		scanf("%d%d", &code, &source);
		v[code] += source;
		if (v[code] >= maxsource) {
			maxcode = code;
			maxsource = v[code];
		}
	}
	printf("%d %d", maxcode, maxsource);
	return 0;
}

1033 旧键盘打字 (20 分)

旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现。现在给出应该输入的一段文字、以及坏掉的那些键,打出的结果文字会是怎样?

输入格式:

输入在 2 行中分别给出坏掉的那些键、以及应该输入的文字。其中对应英文字母的坏键以大写给出;每段文字是不超过 105 个字符的串。可用的字符包括字母 [a-zA-Z]、数字 0-9、以及下划线 _(代表空格)、,.-+(代表上档键)。题目保证第 2 行输入的文字串非空。

注意:如果上档键坏掉了,那么大写的英文字母无法被打出。

输出格式:

在一行中输出能够被打出的结果文字。如果没有一个字符能被打出,则输出空行。

输入样例:

7+IE.
7_This_is_a_test.
结尾无空行

输出样例:

_hs_s_a_tst
结尾无空行

解析:

这道题其实也是字符串处理,并没有什么特别的,和1029题是一样的,用好几个字符串处理函数之后就非常方便。不过这里有一个小细节要注意,就是字符串的输入方式,下面的代码以及注释会给出解释。

代码:

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
	string in, out;
	//由于getline()可输入的字符数组长度大于cin 所以这里用getline而不是cin >> in >> out;
	getline(cin, in);
	getline(cin, out);
	for (int i = 0; i < out.size(); i++) {
		if (in.find(toupper(out[i])) != string::npos) continue;
		if (isupper(out[i]) && in.find("+") != string::npos) continue;
		printf("%c", out[i]);
	}
	return 0;
}

1034 有理数四则运算 (20 分)

本题要求编写程序,计算 2 个有理数的和、差、积、商。

输入格式:

输入在一行中按照 a1/b1 a2/b2 的格式给出两个分数形式的有理数,其中分子和分母全是整型范围内的整数,负号只可能出现在分子前,分母不为 0。

输出格式:

分别在 4 行中按照 有理数1 运算符 有理数2 = 结果 的格式顺序输出 2 个有理数的和、差、积、商。注意输出的每个有理数必须是该有理数的最简形式 k a/b,其中 k 是整数部分,a/b 是最简分数部分;若为负数,则须加括号;若除法分母为 0,则输出 Inf。题目保证正确的输出中没有超过整型范围的整数。

输入样例 1:

2/3 -4/2
结尾无空行

输出样例 1:

2/3 + (-2) = (-1 1/3)
2/3 - (-2) = 2 2/3
2/3 * (-2) = (-1 1/3)
2/3 / (-2) = (-1/3)
结尾无空行

输入样例 2:

5/3 0/6
结尾无空行

输出样例 2:

1 2/3 + 0 = 1 2/3
1 2/3 - 0 = 1 2/3
1 2/3 * 0 = 0
1 2/3 / 0 = Inf
结尾无空行

解析:

这道题其实是对数字的处理,下面po上柳神的代码

代码:

#include <iostream>
#include <cmath>
using namespace std;
long long a, b, c, d;
long long gcd(long long t1, long long t2) {
    return t2 == 0 ? t1 : gcd(t2, t1 % t2);
}
void func(long long m, long long n) {
    if (m * n == 0) {
        printf("%s", n == 0 ? "Inf" : "0");
        return ;
    }
    bool flag = ((m < 0 && n > 0) || (m > 0 && n < 0));
    m = abs(m); n = abs(n);
    long long x = m / n;
    printf("%s", flag ? "(-" : "");
    if (x != 0) printf("%lld", x);
    if (m % n == 0) {
        if(flag) printf(")");
        return ;
    }
    if (x != 0) printf(" ");
    m = m - x * n;
    long long t = gcd(m, n);
    m = m / t; n = n / t;
    printf("%lld/%lld%s", m, n, flag ? ")" : "");
}
int main() {
    scanf("%lld/%lld %lld/%lld", &a, &b, &c, &d);
    func(a, b); printf(" + "); func(c, d); printf(" = "); func(a * d + b * c, b * d); printf("\n");
    func(a, b); printf(" - "); func(c, d); printf(" = "); func(a * d - b * c, b * d); printf("\n");
    func(a, b); printf(" * "); func(c, d); printf(" = "); func(a * c, b * d); printf("\n");
    func(a, b); printf(" / "); func(c, d); printf(" = "); func(a * d, b * c);
    return 0;
}

1035 插入与归并 (25 分)

根据维基百科的定义:

插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。

归并排序进行如下迭代操作:首先将原始序列看成 N 个只包含 1 个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下 1 个有序的序列。

现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?

输入格式:

输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。

输出格式:

首先在第 1 行中输出Insertion Sort表示插入排序、或Merge Sort表示归并排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。

输入样例 1:

10
3 1 2 8 7 5 9 4 6 0
1 2 3 7 8 5 9 4 6 0
结尾无空行

输出样例 1:

Insertion Sort
1 2 3 5 7 8 9 4 6 0
结尾无空行

输入样例 2:

10
3 1 2 8 7 5 9 4 0 6
1 3 2 8 5 7 4 9 0 6
结尾无空行

输出样例 2:

Merge Sort
1 2 3 8 4 5 7 9 0 6
结尾无空行

解析:

这题有两个难点,首先要判断是哪种插入方法,其次还要输出下一轮数组。

代码:

#include <iostream>
#include <algorithm>
using namespace std;
int main() {
	int n,a[100],b[100],i,j;
	scanf("%d", &n);
	for (i = 0; i < n; i++)scanf("%d", &a[i]);
	for (i = 0; i < n; i++)scanf("%d", &b[i]);
	for (i = 0; i < n - 1 && b[i] <= b[i + 1]; i++);
	for (j = i + 1; a[j] == b[j] && j < n; j++);
	if (j == n) {
		cout << "Insertion Sort" << endl;
		sort(a, a + i + 2);
	}
	else {
		cout << "Merge Sort" << endl;
		int k = 1, flag = 1;
		while (flag) {
			flag = 0;
			for (i = 0; i < n; i++) {
				if (a[i] != b[i])
					flag = 1;
			}
			k = k * 2;
			for (i = 0; i < n / k; i++)
				sort(a + i * k, a + (i + 1) * k);
			sort(a + n / k * k, a + n);
		}
	}
	for (j = 0; j < n; j++) {
		if (j != 0) printf(" ");
		printf("%d", a[j]);
	}
	return 0;

}

1036 跟奥巴马一起编程 (15 分)

美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统。2014 年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个正方形。现在你也跟他一起画吧!

输入格式:

输入在一行中给出正方形边长 N(3≤N≤20)和组成正方形边的某种字符 C,间隔一个空格。

输出格式:

输出由给定字符 C 画出的正方形。但是注意到行间距比列间距大,所以为了让结果看上去更像正方形,我们输出的行数实际上是列数的 50%(四舍五入取整)。

输入样例:

10 a
结尾无空行

输出样例:

aaaaaaaaaa
a        a
a        a
a        a
aaaaaaaaaa

解析:

这是属于图形输出类的题目,关键在于按照格式输出。没啥难度,好好设计就行。

代码:

#include <iostream>
#include <math.h>
using namespace std;
int main() {
	int n;
	char c;
	cin >> n >> c;
	int cloum = ceil(n*0.5)-2;
	int t = n;
	while (t--)printf("%c", c);
	printf("\n");
	while (cloum--) {
		printf("%c", c); 
		for (int i = 0; i < n - 2; i++) {
			printf("%c", ' ');	
		}
		printf("%c\n", c);
	}
	t = n;
	while (t--)printf("%c", c);
	return 0;
}

1037 在霍格沃茨找零钱 (20 分)

如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易。”现在,给定哈利应付的价钱 P 和他实付的钱 A,你的任务是写一个程序来计算他应该被找的零钱。

输入格式:

输入在 1 行中分别给出 P 和 A,格式为 Galleon.Sickle.Knut,其间用 1 个空格分隔。这里 Galleon 是 [0, 107] 区间内的整数,Sickle 是 [0, 17) 区间内的整数,Knut 是 [0, 29) 区间内的整数。

输出格式:

在一行中用与输入同样的格式输出哈利应该被找的零钱。如果他没带够钱,那么输出的应该是负数。

输入样例 1:

10.16.27 14.1.28
结尾无空行

输出样例 1:

3.2.1
结尾无空行

输入样例 2:

14.1.28 10.16.27
结尾无空行

输出样例 2:

-3.2.1
结尾无空行

解析:

这题本质上就是进制转换的一道题,但实际上,这样的进制转换不涉及整个数转换,而是类似于进位转换。

代码:

#include <iostream>
int main() {
	int pg, ps, pk, ag, as, ak, rk;
	scanf("%d.%d.%d", &pg, &ps, &pk);
	scanf("%d.%d.%d", &ag, &as, &ak);
	pk += ps * 29 + pg * 17 * 29;
	ak += as * 29 + ag * 17 * 29;
	rk = ak - pk;
	if (rk < 0) {
		printf("-");
		rk = -rk;
	}
	printf("%d.%d.%d", rk / (17 * 29), rk % (17 * 29) / 29, rk % 29);
	return 0;
}

1038 统计同成绩学生 (20 分)

本题要求读入 N 名学生的成绩,将获得某一给定分数的学生人数输出。

输入格式:

输入在第 1 行给出不超过 105 的正整数 N,即学生总人数。随后一行给出 N 名学生的百分制整数成绩,中间以空格分隔。最后一行给出要查询的分数个数 K(不超过 N 的正整数),随后是 K 个分数,中间以空格分隔。

输出格式:

在一行中按查询顺序给出得分等于指定分数的学生人数,中间以空格分隔,但行末不得有多余空格。

输入样例:

10
60 75 90 55 75 99 82 90 75 50
3 75 90 88
结尾无空行

输出样例:

3 2 0
结尾无空行

解析:

这题创建一个散列表,直接用数组下标去访问,空间换时间,简直不要太方便。

代码:

#include <iostream>
using namespace std;
int main() {
	int n, source, search, k, array[101] = {0};
	scanf("%d", &n);
	while (n--) {
		scanf("%d", &source);
		array[source]++;
	}
	scanf("%d", &k);
	scanf("%d", &search);
	printf("%d", array[search]);
	for (int i = 1; i < k; i++) {
		scanf("%d", &search);
		printf(" %d", array[search]);
	}
	return 0;
}

1039 到底买不买 (20 分)

小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如果是,那么告诉她有多少多余的珠子;如果不是,那么告诉她缺了多少珠子。

为方便起见,我们用[0-9]、[a-z]、[A-Z]范围内的字符来表示颜色。例如在图1中,第3串是小红想做的珠串;那么第1串可以买,因为包含了全部她想要的珠子,还多了8颗不需要的珠子;第2串不能买,因为没有黑色珠子,并且少了一颗红色的珠子。

figbuy.jpg

图 1

输入格式:

每个输入包含 1 个测试用例。每个测试用例分别在 2 行中先后给出摊主的珠串和小红想做的珠串,两串都不超过 1000 个珠子。

输出格式:

如果可以买,则在一行中输出 Yes 以及有多少多余的珠子;如果不可以买,则在一行中输出 No 以及缺了多少珠子。其间以 1 个空格分隔。

输入样例 1:

ppRYYGrrYBR2258
YrR8RrY
结尾无空行

输出样例 1:

Yes 8
结尾无空行

输入样例 2:

ppRYYGrrYB225
YrR8RrY
结尾无空行

输出样例 2:

No 2
结尾无空行

解析:

这道题也建立一个散列表就好了,完美解决。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	string in, out;
	int array[123] = { 0 }, more = 0, less = 0;
	cin >> in >> out;
	for (int i = 0; i < in.size(); i++)array[in[i] - '0']++;
	for (int i = 0; i < out.size(); i++)array[out[i] - '0']--;
	for (int i = 0; i < 123; i++) {
		if (array[i] > 0)more += array[i];
		if (array[i] < 0)less += array[i];
	}
	if (less != 0) {
		printf("No %d", -less);
	}else {
		printf("Yes %d", more);
	}
	return 0;
}

1040 有几个PAT (25 分)

字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T);第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位(T)。

现给定字符串,问一共可以形成多少个 PAT

输入格式:

输入只有一行,包含一个字符串,长度不超过105,只包含 PAT 三种字母。

输出格式:

在一行中输出给定字符串中包含多少个 PAT。由于结果可能比较大,只输出对 1000000007 取余数的结果。

输入样例:

APPAPT
结尾无空行

输出样例:

2
结尾无空行

解析:

统计A左边P的个数和A右边T的个数,累加相乘就是PAT的个数。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s;
	getline(cin, s);
	int len = s.length(), result = 0, countp = 0, countt = 0;
	for (int i = 0; i < len; i++) {
		if (s[i] == 'T')
			countt++;
	}
	for (int i = 0; i < len; i++) {
		if (s[i] == 'P') countp++;
		if (s[i] == 'T') countt--;
		if (s[i] == 'A') result = (result + (countp * countt) % 1000000007) % 1000000007;
	}
	cout << result;
	return 0;

}

1041 考试座位号 (15 分)

每个 PAT 考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位。正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座位就座。但有些考生迟到了,试机已经结束,他们只能拿着领到的试机座位号码求助于你,从后台查出他们的考试座位号码。

输入格式:

输入第一行给出一个正整数 N(≤1000),随后 N 行,每行给出一个考生的信息:准考证号 试机座位号 考试座位号。其中准考证号由 16 位数字组成,座位从 1 到 N 编号。输入保证每个人的准考证号都不同,并且任何时候都不会把两个人分配到同一个座位上。

考生信息之后,给出一个正整数 M(≤N),随后一行中给出 M 个待查询的试机座位号码,以空格分隔。

输出格式:

对应每个需要查询的试机座位号码,在一行中输出对应考生的准考证号和考试座位号码,中间用 1 个空格分隔。

输入样例:

4
3310120150912233 2 4
3310120150912119 4 1
3310120150912126 1 3
3310120150912002 3 2
2
3 4
结尾无空行

输出样例:

3310120150912002 2
3310120150912119 1
结尾无空行

解析:

这道题考察的是元素查找,可以看到这里的特征是多个信息绑定,所以我们用到结构体会非常的方便。

代码:

#include <iostream>
using namespace std;
struct Student {
	int cpos, kpos;
	long long code;
}student[1000];
int main() {
	int n,cpos,m,scpos;
	long long code;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%lld%d", &code,&cpos);
		scanf("%d", &student[cpos].kpos);
		student[cpos].code = code;
		student[cpos].cpos = cpos;
	}
	scanf("%d", &m);
	while (m--) {
		scanf("%d", &scpos);
		printf("%lld %d\n", student[scpos].code, student[scpos].kpos);
	}
	return 0;
}

1042 字符统计 (20 分)

请编写程序,找出一段给定文字中出现最频繁的那个英文字母。

输入格式:

输入在一行中给出一个长度不超过 1000 的字符串。字符串由 ASCII 码表中任意可见字符及空格组成,至少包含 1 个英文字母,以回车结束(回车不算在内)。

输出格式:

在一行中输出出现频率最高的那个英文字母及其出现次数,其间以空格分隔。如果有并列,则输出按字母序最小的那个字母。统计时不区分大小写,输出小写字母。

输入样例:

This is a simple TEST.  There ARE numbers and other symbols 1&2&3...........
结尾无空行

输出样例:

e 7
结尾无空行

解析:

也是散列问题,唯一要多注意的是大小写的处理<cctype>类非常有用

代码:

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
bool cmp(int a, int b) { return a > b; };
int main(){
	string s;
	int array[123] = { 0 },max = -1;
	char c;
	getline(cin,s);
	for (int i = 0; i < s.size(); i++) {
		if ((s[i] >= 'a'&&s[i] <= 'z') || (s[i] >= 'A'&&s[i] <= 'Z')) {
			array[tolower(s[i])]++;
		}
	}
	for (int i = 64; i < 123; i++) {
		if (array[i] > max) {
			max = array[i];
			c = i;
		}
	}
	printf("%c %d", c, max);
	return 0;
}

1043 输出PATest (20 分)

给定一个长度不超过 104 的、仅由英文字母构成的字符串。请将字符重新调整顺序,按 PATestPATest.... 这样的顺序输出,并忽略其它字符。当然,六种字符的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按 PATest 的顺序打印,直到所有字符都被输出。

输入格式:

输入在一行中给出一个长度不超过 104 的、仅由英文字母构成的非空字符串。

输出格式:

在一行中按题目要求输出排序后的字符串。题目保证输出非空。

输入样例:

redlesPayBestPATTopTeePHPereatitAPPT
结尾无空行

输出样例:

PATestPATestPTetPTePePee
结尾无空行

解析:

建立一个“PATest”当做字典表数组来查询即可。本质也是考散列。

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
	string s;
	string d = "PATest";
	int out[6] = { 0 }, sum = 0;
	cin >> s;
	for (int i = 0; i < s.size(); i++) {
		if (d.find(s[i]) != string::npos) {
			out[d.find(s[i])]++;
			sum++;
		}
	}
	for (int i = 0; i < sum; i++) {
		if (out[i % 6] != 0) {
			printf("%c", d[i % 6]);
			out[i % 6]--;
		}
		else {
			sum++;
		}
	}
}

1044 火星数字 (20 分)

火星人是以 13 进制计数的:

  • 地球人的 0 被火星人称为 tret。
  • 地球人数字 1 到 12 的火星文分别为:jan, feb, mar, apr, may, jun, jly, aug, sep, oct, nov, dec。
  • 火星人将进位以后的 12 个高位数字分别称为:tam, hel, maa, huh, tou, kes, hei, elo, syy, lok, mer, jou。

例如地球人的数字 29 翻译成火星文就是 hel mar;而火星文 elo nov 对应地球数字 115。为了方便交流,请你编写程序实现地球和火星数字之间的互译。

输入格式:

输入第一行给出一个正整数 N(<100),随后 N 行,每行给出一个 [0, 169) 区间内的数字 —— 或者是地球文,或者是火星文。

输出格式:

对应输入的每一行,在一行中输出翻译后的另一种语言的数字。

输入样例:

4
29
5
elo nov
tam
结尾无空行

输出样例:

hel mar
may
115
13
结尾无空行

解析:

啊啊啊啊这道题是真的麻烦,其实思路就是从火星文转地球,地球转火星,写两个转换函数然后主函数调用就好了。

代码:

#include <iostream>
#include <string>
using namespace std;
string anum[13] = { "tret","jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec" };
string ajin[13] = { "","tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou" };
int len;
string s;
void turnMark(string s) {
	int i = stoi(s);
	if (i / 13)cout<<ajin[i / 13];
	if ((i / 13) && (i % 13)) cout<<" ";
	if (i % 13 || i == 0) cout<<anum[i % 13];
}
void turnEarth(string s) {
	int t1 = 0, t2 = 0;
	string s1 = s.substr(0, 3), s2;
	if (len > 4) s2 = s.substr(4, 3);
	for (int j = 1; j <= 12; j++) {
		if (s1 == anum[j] || s2 == anum[j]) t2 = j;
		if (s1 == ajin[j]) t1 = j;
	}
	cout << t1 * 13 + t2;
}
int main() {
	int n;
	scanf("%d", &n);
	getchar();
	while (n--) {
		getline(cin, s);
		len = s.length();
		if (s[0] >= '0'&&s[0] <= '9') {
			turnMark(s);
		}
		else {
			turnEarth(s);
		}
		cout << endl;
	}
	return 0;
}

1045 快速排序 (25 分)

著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?

例如给定 N=5, 排列是1、3、2、4、5。则:

  • 1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
  • 尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
  • 尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
  • 类似原因,4 和 5 都可能是主元。

因此,有 3 个元素可能是主元。

输入格式:

输入在第 1 行中给出一个正整数 N(≤105); 第 2 行是空格分隔的 N 个不同的正整数,每个数不超过 109。

输出格式:

在第 1 行中输出有可能是主元的元素个数;在第 2 行中按递增顺序输出这些元素,其间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

5
1 3 2 4 5
结尾无空行

输出样例:

3
1 4 5
结尾无空行

解析:

如果直接用循环去遍历的话极有可能超时。

代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int main() {
	int n, v[100001], sum = 0,max =0;
	scanf("%d", &n);
	vector<int> a(n), b(n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
		b[i] = a[i];
	}
	sort(a.begin(), a.end());
	for (int i = 0; i < n; i++) {
		if (a[i] == b[i] && b[i] > max)
			v[sum++] = b[i];
		if (b[i] > max)
			max = b[i];
	}
	printf("%d\n", sum);
	for (int i = 0; i < sum; i++) {
		if (i != 0) printf(" ");
		printf("%d", v[i]);
	}
	printf("\n");
	return 0;
}

1046 划拳 (15 分)

划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输家罚一杯酒。两人同赢或两人同输则继续下一轮,直到唯一的赢家出现。

下面给出甲、乙两人的划拳记录,请你统计他们最后分别喝了多少杯酒。

输入格式:

输入第一行先给出一个正整数 N(≤100),随后 N 行,每行给出一轮划拳的记录,格式为:

甲喊 甲划 乙喊 乙划

其中是喊出的数字,是划出的数字,均为不超过 100 的正整数(两只手一起划)。

输出格式:

在一行中先后输出甲、乙两人喝酒的杯数,其间以一个空格分隔。

输入样例:

5
8 10 9 12
5 10 5 10
3 8 5 12
12 18 1 13
4 16 12 15
结尾无空行

输出样例:

1 2
结尾无空行

解析:

这题也是一道简单模拟题,没有什么难度,只需要去模拟情景分别处理就好了。

代码:

#include <iostream>
using namespace std;
int main() {
	int n;
	scanf("%d", &n);
	int jhan, jhua, yhan, yhua, jhe = 0, yhe = 0;
	while (n--) {
		scanf("%d%d%d%d", &jhan, &jhua, &yhan, &yhua);
		int sum = jhan + yhan;
		if (jhua == yhua)continue;
		if (sum == jhua)yhe++;
		if (sum == yhua)jhe++;
	}
	printf("%d %d", jhe, yhe);
	return 0;
}

1047 编程团体赛 (20 分)

编程团体赛的规则为:每个参赛队由若干队员组成;所有队员独立比赛;参赛队的成绩为所有队员的成绩和;成绩最高的队获胜。

现给定所有队员的比赛成绩,请你编写程序找出冠军队。

输入格式:

输入第一行给出一个正整数 N(≤104),即所有参赛队员总数。随后 N 行,每行给出一位队员的成绩,格式为:队伍编号-队员编号 成绩,其中队伍编号为 1 到 1000 的正整数,队员编号为 1 到 10 的正整数,成绩为 0 到 100 的整数。

输出格式:

在一行中输出冠军队的编号和总成绩,其间以一个空格分隔。注意:题目保证冠军队是唯一的。

输入样例:

6
3-10 99
11-5 87
102-1 0
102-3 100
11-9 89
3-2 61
结尾无空行

输出样例:

11 176
结尾无空行

解析:

直接散列解决!冲!SOOOOOOEASY!

代码:

#include <iostream>
using namespace std;
int main() {
	int n, tmp[1001] = { 0 },tcode,pcode,source,maxt,maxs=-1;
	scanf("%d", &n);
	while (n--) {
		scanf("%d-%d", &tcode, &pcode);
		scanf("%d", &source);
		tmp[tcode] += source;
		if (tmp[tcode] > maxs) {
			maxt = tcode;
			maxs = tmp[tcode];
		}
	}
	printf("%d %d", maxt, maxs);
	return 0;
}

1048 数字加密 (20 分)

本题要求实现一种数字加密方法。首先固定一个加密用正整数 A,对任一正整数 B,将其每 1 位数字与 A 的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对 13 取余——这里用 J 代表 10、Q 代表 11、K 代表 12;对偶数位,用 B 的数字减去 A 的数字,若结果为负数,则再加 10。这里令个位为第 1 位。

输入格式:

输入在一行中依次给出 A 和 B,均为不超过 100 位的正整数,其间以空格分隔。

输出格式:

在一行中输出加密后的结果。

输入样例:

1234567 368782971
结尾无空行

输出样例:

3695Q8118
结尾无空行

解析:

兄弟们!这道题就是字符串解析的问题,但是真的狗的是,当A,B两数长度不一致的时候要补上0进行处理!一定要注意这个细节!

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
		string a, b, result = "";
		int pos = 1;
		char array[13] = { '0','1','2','3','4','5','6','7','8','9','J','Q','K' };
		cin >> a >> b;
		if (a.size() < b.size()) {
			a.insert(0, b.size() - a.size(), '0');
		}
		else {
			b.insert(0, a.size() - b.size(), '0');
		}
		for (int i = b.size() - 1; i >= 0; i--) {
			if (pos % 2 == 1) {
				result = array[((a[i] - '0') + (b[i] - '0')) % 13] + result;
			}
			else {
				int w = (b[i] - '0') - (a[i] - '0') >= 0 ? (b[i] - '0') - (a[i] - '0') : (b[i] - '0') - (a[i] - '0') + 10;
				result = to_string(w) + result;
			}
			pos++;
		}
		cout << result;
	return 0;
}

1049 数列的片段和 (20 分)

给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列 { 0.1, 0.2, 0.3, 0.4 },我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) (0.4) 这 10 个片段。

给定正整数数列,求出全部片段包含的所有的数之和。如本例中 10 个片段总和是 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0。

输入格式:

输入第一行给出一个不超过 105 的正整数 N,表示数列中数的个数,第二行给出 N 个不超过 1.0 的正数,是数列中的数,其间以空格分隔。

输出格式:

在一行中输出该序列所有片段包含的数之和,精确到小数点后 2 位。

输入样例:

4
0.1 0.2 0.3 0.4
结尾无空行

输出样例:

5.00
结尾无空行

解析:

这题找到数学规律就可以了,本质上以上输入例子可化简为 0.14+0.2(3+3)+0.3*(2+2+2)+0.4*(0.1+0.1+0.1+0.1)

代码:

#include <iostream>
using namespace std;
int main() {
	int n, i = 1;
	double num;
	long long result = 0;
	scanf("%d", &n);
	while (n){
		scanf("%lf", &num);
		result += (long long)(num*1000)*n*i;
		i++;
		n--;
	} 
	printf("%.2lf", result/1000.0);
}

1050 螺旋矩阵 (25 分)

本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第 1 个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列,满足条件:m×n 等于 N;m≥n;且 m−n 取所有可能值中的最小值。

输入格式:

输入在第 1 行中给出一个正整数 N,第 2 行给出 N 个待填充的正整数。所有数字不超过 104,相邻数字以空格分隔。

输出格式:

输出螺旋矩阵。每行 n 个数字,共 m 行。相邻数字以 1 个空格分隔,行末不得有多余空格。

输入样例:

12
37 76 20 98 76 42 53 95 60 81 58 93
结尾无空行

输出样例:

98 95 93
42 37 81
53 20 76
58 60 76
结尾无空行

解析:

这是一道难度比较高的模拟,其实本来呢按照正常的顺序模拟输出就好了,可是我觉得很复杂很麻烦。疲于思考。

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
int cmp(int a, int b) { return a > b; }
int main() {
	int N, m, n, t = 0;
	scanf("%d", &N);
	for (n = sqrt((double)N); n >= 1; n--) {
		if (N % n == 0) {
			m = N / n;
			break;
		}
	}
	vector<int> a(N);
	for (int i = 0; i < N; i++)
		scanf("%d", &a[i]);
	sort(a.begin(), a.end(), cmp);
	vector<vector<int> > b(m, vector<int>(n));
	int level = m / 2 + m % 2;
	for (int i = 0; i < level; i++) {
		for (int j = i; j <= n - 1 - i && t <= N - 1; j++)
			b[i][j] = a[t++];
		for (int j = i + 1; j <= m - 2 - i && t <= N - 1; j++)
			b[j][n - 1 - i] = a[t++];
		for (int j = n - i - 1; j >= i && t <= N - 1; j--)
			b[m - 1 - i][j] = a[t++];
		for (int j = m - 2 - i; j >= i + 1 && t <= N - 1; j--)
			b[j][i] = a[t++];
	}
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			printf("%d", b[i][j]);
			if (j != n - 1) printf(" ");
		}
		printf("\n");
	}
	return 0;
}