PAT乙级题解

写在前面

  • 本文为PTA乙级题解,所有问题均采用C++代码实现。由于对C++的理解尚未深入,且第一目标为通过测试,故代码中出现的不合理之处敬请谅解并欢迎指出,定当虚心接受并加以改正。
  • 试题集中存在着一些相对基础的题目,并不包含重要的算法等知识,代码实现相对简单。为方便后续复习,减少不必要的阅读,此类题目不予记录。
  • 随着PAT考试的不断进行,PTA以及题库也在不断更新,本文也会第一时间更新。
  • 于2020年6月12日完成了乙级的所有题目,加油!

PAT乙级题解_第1张图片

1003 我要通过 (20分)

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

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

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

输入格式

每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 n (<10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。

输出格式

每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出 YES,否则输出 NO

输入样例

8
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA

输出样例

YES
YES
YES
YES
NO
NO
NO
NO

解题思路

  1. 这种字符串的问题一定要先找到规律,而不是强行模拟
  2. 观察样例,会得到YES的要求是
    • 只有一个P和T(字符数为1),不能没有A(字符数不为0),不能有其他字符(字符种类为3),P和T之间必须有A(PT下标之差不能为1),
    • 中间多加一个A,后面就多加一份前面的A,所以就是前面A的个数×中间A的个数,等于后面A的个数

代码实现

#include
#include
#include
using namespace std;
int main()
{
	int num;
	cin >> num;
	string str;
	for (int i = 0; i < num; i++)
	{
		cin >> str;
		int p, t;
       	 map<char, int>mp;//每个字符串都要重新统计
		for (int j = 0; j< str.size(); j++)
		{
			mp[str[j]]++;
			if (str[j] == 'P') p = j;
			if (str[j] == 'T') t = j;
		}
		if (mp['P'] == 1 && mp['T'] == 1 && mp['A'] != 0 && (t - p) != 1 &&mp.size()==3&& p*(t - p - 1) == str.length() - t - 1)
			cout << "YES" << endl;
		else cout << "NO" << endl;

	}
		return 0;
}

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

设计函数求一元多项式的导数

输入格式

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

输出格式

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

输入样例

3 4 -5 2 6 1 -2 0

输出样例

12 3 -10 1 6 0

解题思路

  • 首先有个数学概念自己没掌握:多项式是默认没有负指数的,有负指数的那个叫做分式
  • 实现起来很简单,主要还是读懂题意的意思,所以一旦输入第一项的指数是0,说明就只有这一项,那么由它产生的结果一定是0多项式,就输出0 0即可,后面的系数为0的项则不要输出。

代码实现

#include 
using namespace std;

int main() {	
    int cof,exp;
    int count = 0;
	while (cin >> cof >> exp)
	{
	    count++;
		if (exp == 0&&count==1)	cout << 0 << " " << 0;
		cof = cof*exp;
	    exp -= 1;
		if (cof == 0)		continue;
		else {
            if(count==1)    cout << cof << " " << exp;
			else cout<< " " << cof << " " << exp;
		}
	}
	return 0;
}

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

大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm。大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间星期四 14:04,因为前面两字符串中第 1 对相同的大写英文字母(大小写有区分)是第 4 个字母 D,代表星期四;第 2 对相同的字符是 E ,那是第 5 个英文字母,代表一天里的第 14 个钟头(于是一天的 0 点到 23 点由数字 0 到 9、以及大写字母 AN 表示);后面两字符串第 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

解题思路

按各个部分进行模拟,两个坑点在于处理小时和处理分钟的时候,大于G和大于N的字符是不能接受的

代码实现

#include
#include
#include
#include
using namespace std;

int main() {
	int num;
	string s1, s2, s3, s4;
	cin >> s1 >> s2 >> s3 >> s4;
	map<char, string>day;
	day['A'] = "MON"; day['B'] = "TUE"; day['C'] = "WED"; day['D'] = "THU"; day['E'] = "FRI"; day['F'] = "SAT"; day['G'] = "SUN";

	int i = 0;
	for (; i < s1.size() && i < s2.size(); i++)
	{
		//注意:G后面的大写字母是不能接收的
		if (s1[i] == s2[i] && isalpha(s1[i]) && isupper(s1[i])&&s1[i]<='G')
		{
			cout << day[s1[i]] << " ";
			break;
		}
	}
	for(int j = i+1;j<s1.size()&&j<s2.size();j++)
	{

		if (s1[j] == s2[j])
		{	//注意N后面的字符也是不能接收的
			if (isalpha(s1[j]) && isupper(s1[j])&&s1[j]<='N')
			{
				printf("%02d:", s1[j] - 'A' + 10);
				break;
			}
			else if (isdigit(s1[j]))
			{
				printf("%02d:", s1[j] - '0');
				break;
			}
		}
	}
	for (int i = 0; i < s3.size() && i < s4.size(); i++)
	{
		if (s3[i] == s4[i] && isalpha(s3[i]))
		{
			printf("%02d", i);
			break;
		}
	}
	
	return 0;
}

1015 德才论 (25分)

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

输入格式

输入第一行给出 3 个正整数,分别为:N(≤10^5),即考生总数;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

解题思路

多么简单的一道模拟题,败在了cin,cout的超时上,再用cin,cout处理大数据就是狗

代码实现

#include
#include
#include
using namespace std;
struct node {
	int number;
	int de;
	int cai;
	int sum;
	int rank;
};
int cmp(node a, node b)
{
	if (a.rank == b.rank)
	{
		if (a.sum == b.sum)
		{
			if (a.de == b.de)
				return a.number < b.number;
			else return a.de > b.de;
		}
		else return a.sum > b.sum;
	}
	else return a.rank < b.rank;
}
int main()
{
	int num, a,b;
	cin >> num >> a >> b;
	int count = 0;
	vector<node>n(num);
	for (int i = 0;i<num;i++)
	{
		scanf("%d %d %d",&n[i].number,&n[i].de,&n[i].cai);
		n[i].sum = n[i].de + n[i].cai;
		if (n[i].de >= b && n[i].cai >= b)
			n[i].rank = 1;
		else if (n[i].de >= b && n[i].cai < b&&n[i].cai >= a)
			n[i].rank = 2;
		else if (n[i].de < b&&n[i].cai < b && n[i].de >= a && n[i].cai >= a && n[i].de >= n[i].cai)
			n[i].rank = 3;
		else if (n[i].de >= a && n[i].cai >= a && n[i].de < n[i].cai)
			n[i].rank = 4;
		else {
			n[i].rank = 5;
			count++;
		}
	}
	sort(n.begin(), n.end(),cmp);
	cout << n.size() - count << endl;
	for (int i = 0; i < n.size() - count; i++)
		printf("%d %d %d\n",n[i].number,n[i].de,n[i].cai);
	return 0;
}

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,104) 区间内的正整数 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

解题思路

很常规的模拟,唯独一个测试点没有考虑到,就是当输入的数是6174本身时,所以要用do while循环而不是while

代码实现

#include 
#include 
#include
#include
using namespace std;

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

int main() {	
	string end = "6174";
	string num;
	cin >> num;

	int big, small;
	while (num.size() != 4)
		num.push_back('0');
    //不用do while 当输入6174时就不输出了
	do
	{
		sort(num.begin(), num.end());
		small = stoi(num);
		sort(num.begin(), num.end(), cmp);
		big = stoi(num);
		if (big - small == 0)
		{
			printf("%04d - %04d = 0000", big, big);
			return 0;
		}
		printf("%04d - %04d = %04d\n", big, small, big - small);
		num = to_string(big - small);
		while (num.size() != 4)
			num.push_back('0');
	} while (num != end);
	
	return 0;
}

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

解题思路

分好类别,各自讨论

  1. 先把数字部分读取进来,放在digit中,然后将指数的绝对值读取进来,放在cishu中
  2. 按照推导出的规律,指数为负时,向前添0,指数为正时,向后添0
    • 掉坑点,指数为正时,如果指数比有效数字小,此时不用添0

代码实现

#include 
#include
using namespace std;

int main() {
	string s;
	cin >> s;
	char yesorno;
	string num;
	int flag = 0;
	string dig;
	int cou = 0;
    //把数字提取出来
	while (s[cou] != 'E')
	{
		if (s[cou] != '.'&&s[cou] != '+'&&s[cou] != '-')
			dig.push_back(s[cou]);
		cou++;
	}

	//把指数提取出来
	for (int i = 0; i < s.size(); i++)
	{

		if (s[i] == 'E')        			yesorno = s[i + 1];
		else continue;
		for (int j = i + 2; j < s.size(); j++)
		{
			if (s[j] == '0'&&flag == 1)		num.push_back(s[j]);
			else if (s[j] != 0)
			{
				flag = 1;
				num.push_back(s[j]);
			}
		}
		break;
	}
	int cishu = stoi(num);

	string ans;
	ans = dig;
	string temps = "0.";
	//分情况讨论
	if (yesorno == '+')
	{
        //如果数字有效数字很多,不用添0,直接适当位置插入小数点即可
		if (ans.size() > cishu + 1)		    ans.insert(ans.begin()+cishu+1, '.');
        //有效数字不够,需要添0
		else    while (ans.size() < cishu + 1)  ans.push_back('0');
	}
	else
	{	//负指数,需要向前添0
		while (temps.size() < cishu + 1)	temps.push_back('0');
		ans = temps + ans;
	}
	if (s[0] == '+')		cout << ans;
	else                    cout << "-" << ans;

	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

解题思路

基本思路已经掌握,唯一的小细节在于,如果结点不在链表中,逆转的时候,可能出现明明已经不够逆转的个数了,但是因为没有减掉,导致进行了本不该的逆转

代码实现

#include 
#include 
#include 
using namespace std;

struct node {
	int value;
	int next;
};
struct ansnode {
	int name;
	int value;
};
int main() {
	int firstname, num, k;
	cin >> firstname >> num >> k;
	vector<node>g(100005);
	for (int i = 0; i < num; i++)
	{
		int tempname;
		cin >> tempname;
		cin >> g[tempname].value >> g[tempname].next;
	}
	vector<ansnode>ans;
	int sum = 0;
	while (firstname != -1)
	{
		ansnode tempnode;
		tempnode.name = firstname;
		tempnode.value = g[firstname].value;
		ans.push_back(tempnode);
		firstname = g[firstname].next;
		sum++;//不在链表中的元素不能统计,会影响最后一次逆转是否进行
	}
	int i = 0;
	while (sum >= k)
	{
		reverse(ans.begin() + i, ans.begin() + i + k);
		i += k;
		sum -= k;
	}
	for (int i = 0; i < ans.size(); i++)
	{
		if (i != ans.size() - 1)
			printf("%05d %d %05d\n", ans[i].name,ans[i].value,ans[i+1].name);
		else printf("%05d %d -1", ans[i].name, ans[i].value);
	}
	

	return 0;
}

1030 完美数列 (25分)

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

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

输入格式

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

输出格式

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

输入样例

10 8
2 3 20 4 5 1 6 7 8 9

输出样例

8

解题思路

  1. 先排序自然会想到,然后从小到大每个点都做一次最小值,然后移动最大值,刚开始肯定符合条件,每次符合条件时判断一下和之前统计的个数谁大,如果更大就更新,当最大值达到一定程度就不符合要求了,退出循环,换新的最小值继续。
  2. 暴力法暴力在,当完成一次最小值的循环后,下一次不必在最小值处开始比较,而是在上一个满足的末尾处下一个开始比较即可,因为上一个那么小的数,有那么最大值都小于他,这次最小值变大了,上次小于他的那些数肯定还小于他,所以从上一次的末尾处的下一个开始比较即可,正好就是 i + count下标处开始

代码实现

#include 
#include 
#include 
using namespace std;

int main() {
	int num;
    long p;
	cin >> num >> p;
	vector<int>ans(num);
	for (int i = 0; i < num; i++)
		scanf("%d", &ans[i]);
	sort(ans.begin(), ans.end());
	int i, j;
	int count=0;
	for (i = 0;i<num;i++)
	{
        //从上一次的末尾开始即可
		for (j = i+count; j < num; j++)
		{
            //发现不符合,中断,进行下一次循环
			if (ans[j] > p*ans[i]) 	break;
            //每次符合的,都要进行比较,然后更新最大值
			if(j-i+1>count)         count = j-i+1;
		}
	}

	cout <<count;
	return 0;
}

1033 旧键盘打字 (20分)

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

输入格式

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

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

输出格式

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

输入样例

7+IE.
7_This_is_a_test.

输出样例

_hs_s_a_tst

解题思路

正常模拟没有问题,一个坑点在于,第一个字符串可能为空,即没有一个键是坏的,题干中也提到保证第2行不为空,应该想到第一行为空,这样cin就无法读入空格,应该采用getline,需要提高这种对题干的警觉性

代码实现

#include
#include
#include
using namespace std;

int main() {
	string s;
	getline(cin,s);
	map<char, int>broken;

	for (int i = 0; i < s.size(); i++)
	{
		broken[s[i]] = 1;
		if (isupper(s[i]))
		{
			s[i] = tolower(s[i]);
			broken[s[i]] = 1;
		}
	}

	string ss;
	cin>>ss;
	int count = 0;
	for (int i = 0; i < ss.size(); i++)
	{
	
			if (broken['+'])
			{

				if (broken[ss[i]])			continue;
				else if (isupper(ss[i]))	continue;
				else {
					cout << ss[i];
					count++;
				}
			}
			else {
				if (broken[ss[i]])          continue;
				else 
                {
					cout << ss[i]; 
                    count++;
				}
		    }
		}

	
	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

解题思路

按照题目要求,逐一实现要求即可,需要注意两点

  • 化简时,不要用暴力法,同时除以最大公约数即可,并且计算最大公约数时也不要用暴力法,会超时,可以选择辗转相除法
  • 虽然题目中说保证输出在整形范围之内,但是计算过程因为乘法的存在,可能会超过类型范围,所以所有数据都要用 int 存储,包括最开始的输入,不然会损失精度,造成错误

代码实现

#include 
#include
#include
#include
using namespace std;

long long gcd(long long a, long long b)
{
	//辗转相除法求最大公约数,然后同时除以最大公约数就可以化简了
    //复杂度比暴力法低,不然的话,会超时
	if (b < 0)
	{
		a = -a;
		b = -b;
	}
	long long temp;
	while (b != 0)
	{
		temp = b;
		b = a % b;
		a = temp;
	}
	return a;
}
void PrintBest(long long  a, long long  b)
{
	if(a!=0)
    {
	long long g = gcd(abs(a), b); 
	a /= g;
	b /= g;
    }
	
	if (a > 0)
	{
		if (a > b)
		{
			cout << a / b;
			if(a%b!=0)		cout<< " " << a % b<<"/"<<b;
		}
		else if (a == b)	cout << 1;
		else            	cout << a << "/" << b;
	}
	else if (a == 0)        cout << 0;
	else
	{
		cout << "(";
		if (-a > b)
		{
			cout << a / b;
			if(-a%b!=0)		cout<<" " << -a % b<<"/"<<b;
		}
		else if (-a == b)	cout << -1;
		else    			cout << a << "/" << b;
		cout << ")";
	}
}
int main() {
    //虽然题目说不超过整型范围数据,但是计算过程可能会出现超出范围的,如果不用long long会损失精度
	long long a1, b1, a2, b2;
	scanf("%ld/%ld %ld/%ld", &a1, &b1, &a2, &b2);
	long long he1, he2, cha1, cha2, shang1, shang2, ji1, ji2;
    
	he1 = a1 * b2 + a2 * b1; he2 = b1 * b2;
	cha1 = a1 * b2 - a2 * b1; cha2 = b1 * b2;
	ji1 = a1 * a2; ji2 = b1 * b2;
	shang1 = a1 * b2; shang2 = a2 * b1;
    
	if (shang2 < 0)
	{
		shang2 = -shang2;
		shang1 = -shang1;
	}

	PrintBest(a1, b1);	cout << " + ";	PrintBest(a2, b2);	cout << " = ";	PrintBest(he1, he2);
	cout << endl;

    PrintBest(a1, b1);	cout << " - ";	PrintBest(a2, b2);	cout << " = ";	PrintBest(cha1,cha2);
	cout << endl;

	PrintBest(a1, b1);	cout << " * ";	PrintBest(a2, b2);	cout << " = ";	PrintBest(ji1, ji2);
	cout << endl;

	PrintBest(a1, b1);	cout << " / ";	PrintBest(a2, b2);	cout << " = ";
	if (shang2 != 0)	PrintBest(shang1, shang2);
	else            	cout << "Inf";
    
	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

解题思路

  1. 判断依据:插入排序前面有序,后面不动
  2. 插入排序下一步:范围扩大1,再排一次,有一种情况,扩大后的范围已经拍好序,需要再扩大,直至是没有排好序的情况
  3. 归并排序下一步:重点是找到几归并,从原始状态不断归并,直到和已知状态相同,再做一次归并即可

代码实现

#include 
#include
#include
#include
using namespace std;

int main()
{
	int num;
	cin >> num;
	vector<int>num1(num);
	vector<int>num2(num);
	//读入数据
	for (int i = 0; i < num; i++)
		cin >> num1[i];
	for (int i = 0; i < num; i++)
		cin >> num2[i];
	int local;
	int QorM = 0;
	//如果后面没有处理,前面还有序,则为插入,否则为归并
	for (int i = num - 1; i >= 1; i--)
	{
		if (num1[i] != num2[i])
		{
			local = i;
			for (i; i >= 1; i--)
			{
				if (num2[i] < num2[i - 1])
				{
					QorM = 1;
					break;
				}
			}
		}
		if (QorM == 1)			break;
	}

	if (QorM == 1)
	{
		cout << "Merge Sort" << endl;
		int k = 1, flag = 1;
		//从初始状态进行模拟,到给定状态后,再模拟一次
		while (flag) {
			flag = 0;
			//开始做一遍2归并,如果flag==0,说明已经到这个状态了,再做一次,下一次就可以退出了
			//如果不等于0.说明这次做完,还要判断一下是否到达状态
			for (int i = 0; i < num; i++)
				if (num1[i] != num2[i])
					flag = 1;
			k = k * 2;
			//把能排序的排序,最后一部分一起排序
			for (int i = 0; i < num / k; i++)		sort(num1.begin() + i * k, num1.begin() + (i + 1) * k);
			sort(num1.begin() + num / k * k, num1.end());
		}


		for (int i = 0; i < num1.size(); i++)
		{
			if (i == 0)			cout << num1[i];
			else                cout << " " << num1[i];
		}
	}
	else {
		cout << "Insertion Sort" << endl;
		//如果是插入排序,如果不同位之后一位比前一位值大,说明已经排好序,应该再向后挪一位,再排序
		while (num2[local + 1] > num2[local])
			local++;
		sort(num2.begin(), num2.begin() + local + 2);

		for (int i = 0; i < num2.size(); i++)
		{
			if (i == 0)		    cout << num2[i];
			else                cout << " " << num2[i];
		}
	}
	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

解题思路

本题不应算作难题,但暴露了自己思维上的问题。

  1. 显然,如果用直接模拟的方法,一定会造成超时,所以要思考背后的数学关系,即对于每个A,其前面P的个数乘以后面T的个数再求和即为所求,在A出现之前,统计P,可以直接P++,但是出现一个T,意味着下一个A的后面的T要少一个
  2. 对1000000007取余这件事,因为相乘的数可能过大,所以要取余,并且加上sum后也可能超过1000000007,所以还要再次取余

代码实现

#include
using namespace std;
int countPAT(string s)
{
	int countt = 0;
	int countp = 0;
	int sum = 0;
	for (auto x : s)
		if (x == 'T')
			countt++;
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] == 'P')	countp++;
		if (s[i] == 'T')	countt--;
		if (s[i] == 'A')    sum =(sum +( countp * countt )% 1000000007)%1000000007;
	}
	return sum ;
}
int main()
{
	string str;
	cin >> str;
	cout << countPAT(str);
	return 0;
}

1045 快速排序 (25分)

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

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

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

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

输入格式

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

输出格式

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

输入样例

5
1 3 2 4 5

输出样例

3
1 4 5

解题思路

  1. 因为数字各不相同,如果一个数是主元必定保证他在正确的位置上,这是首要的前提,然后当他比左边的所有数都大时,自然保证了右边所有的数都比他小,因为数字的个数是一定的,所以先搞一个数组排序过的数组和他比较,如果相等,再判断是不是比左边的最大值大,在这个过程中不断更新最大值
  2. 最后输出的时候,按照题目的要求输出,会有一个测试点不通过,据说是当0个主元的时候要输出一个空行,PAT的格式问题经常有这种小坑,要学会试探

代码实现

#include
#include
#include
using namespace std;
int main()
{
	int num;
	cin >> num;
	vector<int>given(num);
	vector<int>ans(num);
	vector<int>a;
	for (int i = 0; i < num; i++)
	{
		cin >> given[i];
		ans[i] = given[i];
	}
	
	sort(ans.begin(), ans.end());
	int max = 0;
	for (int i = 0; i < num; i++)
	{
		if (given[i] == ans[i] && max < given[i])
			a.push_back(given[i]);
		if (given[i] > max)
			max = given[i];
	}
	cout << a.size() << endl;
	for (int i = 0; i < a.size(); i++)
		if (i == 0)
			cout << a[i];
		else cout << " " << a[i];
	if(a.size()==0)
        cout<<endl;
		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

解题思路

属于数学问题找到对应公式即可,共N项,第 i 项出现的次数由两部分组成,一部分是 i 之前的项包括i的次数,(N - ( i - 1)) * ( i - 1),即次数×遍数,第二部分是 i 开头出现的次数,为N - ( i - 1 )次,合并之后为且将 i 从1改为从0开始,为( N - i ) * ( i + 1 )次,再乘以值,求和即为所求。

代码实现

#include

using namespace std;

int main()
{
	int num;
	cin >> num;
	double sum;
    double temp;
	for (int i = 0; i < num; i++)
	{
		cin >> temp;
		sum += temp * (num - i)*(i + 1);
	}	
	printf("%.2f",sum);
	return 0;
}

1050 螺旋矩阵 (25分)

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

输入格式

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

输出格式

输出螺旋矩阵。每行 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
#include
#include
#include
using namespace std;

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

int main()
{
	int num;
	cin >> num;
	
	vector<int>ans(num);	
    //输入数据后,按大小排序
	for (int i = 0; i < num; i++)
		cin >> ans[i];
	sort(ans.begin(), ans.end(), cmp);
    //计算m和n的值
	int m, n;
	n = sqrt(num);
	for (; n >0; n--)
		if (num%n == 0)
		{
			m = num / n;
			break;
		}

	int i, j, k = 0;
    //建立数组存储答案
	vector<vector<int>>a(m,vector<int>(n));
    //边界不断缩小,直到数据读完
	for (i = 0; k < num; i++) 
    {	
        //从左向右,0行开始,到第一行结束
		for (j = i; j < n - i && k < num; j++) 
            a[i][j] = ans[k++];
        //从上到下,从第二行末尾开始,到最后一列结束
		for (j = i + 1; j < m - i && k < num; j++)	
            a[j][n - i - 1] = ans[k++];
        //从右至左,到最后一行的开头结束
		for (j = n - i - 2; j >= i && k < num; j--)	
            a[m - i - 1][j] = ans[k++];
        //从下至上,到第一列的开头前结束
		for (j = m - 2 - i; j >= i + 1 && k < num; j--) 
            a[j][i] = ans[k++];
        //边界缩小,进行下一轮
	}
    
	for (int i = 0; i < m; i++) 
    {
		for (int j = 0; j < n; j++) 
        {
			cout << a[i][j];
			if (j != n - 1) cout << " ";
		}
			cout << endl;
	}
		return 0;
}

1051 复数乘法 (15分)

复数可以写成 (A+B*i) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i^2=−1;也可以写成三角形式 R(cos(P)+isin(P))。

现给定两个复数的 RP,要求输出两数乘积的常规形式。

输入格式

输入在一行中依次给出两个复数的 R1, P1, R2, P2,数字间以空格分隔。

输出格式

在一行中按照 A+Bi 的格式输出两数乘积的常规形式,实部和虚部均保留 2 位小数。注意:如果 B 是负数,则应该写成 A-|B|i 的形式。

输入样例

2.3 3.5 5.2 0.4

输出样例

-8.68-8.23i

解题思路

别看这题分少,但是坑点多多

  • 起先我先把两个复数化简成标准形式,然后求乘积,切记不要这么做,这样会严重损失精度
  • 先求通式,然后代入,但是在输出的时候会出现一个坑点,就是0.003,0.006这种值,是应该被扔掉的,而且-0.003,-0.006这样的数都要变成0.00,加上这样的条件后,就可以通过了

代码实现

#include 
#include
using namespace std;

int main() {	
	double a, b;
	double r1, p1, r2, p2;
	int temp;
	
	cin >> r1 >> p1 >> r2 >> p2;
	a = r1 * r2*(cos(p1)*cos(p2) - sin(p1)*sin(p2));
	b = r1 * r2*(cos(p1)*sin(p2) + sin(p1)*cos(p2));
	if(abs(a)<0.01)
        cout<<"0.00";
    else printf("%.2f",a);
    if(abs(b)<0.01)
        cout<<"+0.00i";
    else 
    {
        if(b>0)     printf("+%.2fi",b);
        else        printf("-%.2fi",-b);
    }
	return 0;
}

1060 爱丁顿数 (25分)

英国天文学家爱丁顿很喜欢骑车。据说他为了炫耀自己的骑车功力,还定义了一个“爱丁顿数” E ,即满足有 E 天骑车超过 E 英里的最大整数 E。据说爱丁顿自己的 E 等于87。

现给定某人 N 天的骑车距离,请你算出对应的爱丁顿数 E(≤N)。

输入格式

输入第一行给出一个正整数 N (≤10^5),即连续骑车的天数;第二行给出 N 个非负整数,代表每天的骑车距离。

输出格式

在一行中给出 N 天的爱丁顿数。

输入样例

10
6 7 6 9 3 10 8 2 7 8

输出样例

6

解题思路

按照行程数从大到小排序,当第一天行程大于1,说明至少有一天大于1,爱丁顿数暂时为1,当第二天大于2,说明至少有两天大于2,爱丁顿数暂时为2,而当某天的行程不大于n,说明只有n-1天大于n,n不能是爱丁顿数,最大的爱丁顿数应该是n-1

代码实现

#include
#include
#include

using namespace std;
int cmp(int a, int b)
{
	return a > b;
}
int main()
{
	int num;
	cin >> num;
	vector<int>g(num);
	for (int i = 0; i < num; i++)
		cin >> g[i];
	sort(g.begin(), g.end(),cmp);
	int max = 0;
	for (int i = 0; i < num; i++)
	{
		if (g[i] > i + 1)
			max++;
		else break;
	}
	cout << max;

	return 0;
}

1062 最简分数 (20分)

一个分数一般写成两个整数相除的形式:N/M,其中 M 不为0。最简分数是指分子和分母没有公约数的分数表示形式。

现给定两个不相等的正分数 N1/M1 和 N2/M2,要求你按从小到大的顺序列出它们之间分母为 K 的最简分数。

输入格式

输入在一行中按 N/M 的格式给出两个正分数,随后是一个正整数分母 K,其间以空格分隔。题目保证给出的所有整数都不超过 1000。

输出格式

在一行中按 N/M 的格式列出两个给定分数之间分母为 K 的所有最简分数,按从小到大的顺序,其间以 1 个空格分隔。行首尾不得有多余空格。题目保证至少有 1 个输出。

输入样例

7/18 13/20 12

输出样例

5/12 7/12

解题思路

唯一的细节在题干处,题干并没有说给定的两个分数谁大谁小,不要想当然,要善于从题干中挖掘坑点

代码实现

#include 
#include 
using namespace std;
int yes(int a, int b)
{
	if (a > b)
	{
		int temp = a;
		a = b;
		b = temp;
	}
	int ans = 1;
	for (int i = 2; i <= a; i++)
	{
		if (a%i == 0 && b%i == 0)
		{
			ans = 0;
			break;
		}
	}
	return ans;
}
int main() {
	int a1, a2, b1, b2, k;
	scanf("%d/%d %d/%d %d", &a1, &a2, &b1, &b2, &k);
	double d1, d2;
	d1 = static_cast<double>(a1) / static_cast<double>(a2);
	d2 = static_cast<double>(b1) / static_cast<double>(b2);
	vector<int>ans;
	for (int i = 1; i <10000; i++)
	{
		double c = static_cast<double>(i) / static_cast<double>(k);
        //并没有说给定的两个分数谁大谁小,所以都要考虑
		if (((c > d1&&c < d2)||(c > d2&&c < d1))&&yes(i, k))
			ans.push_back(i);
	}
	for (int i = 0; i < ans.size(); i++)
	{
		if (i == 0)	printf("%d/%d", ans[i], k);
		else printf(" %d/%d", ans[i], k);
	}
	return 0;
}

1068 万绿丛中一点红 (20分)

对于计算机而言,颜色不过是像素点对应的一个 24 位的数值。现给定一幅分辨率为 M×N 的画,要求你找出万绿丛中的一点红,即有独一无二颜色的那个像素点,并且该点的颜色与其周围 8 个相邻像素的颜色差充分大。

输入格式

输入第一行给出三个正整数,分别是 MN(≤ 1000),即图像的分辨率;以及 TOL,是所求像素点与相邻点的颜色差阈值,色差超过 TOL 的点才被考虑。随后 N 行,每行给出 M 个像素的颜色值,范围在 [0,224) 内。所有同行数字间用空格或 TAB 分开。

输出格式

在一行中按照 (x, y): color 的格式输出所求像素点的位置以及颜色值,其中位置 xy 分别是该像素在图像矩阵中的列、行编号(从 1 开始编号)。如果这样的点不唯一,则输出 Not Unique;如果这样的点不存在,则输出 Not Exist

输入样例1

8 6 200
0 	 0 	  0 	   0	    0 	     0 	      0        0
65280 	 65280    65280    16711479 65280    65280    65280    65280
16711479 65280    65280    65280    16711680 65280    65280    65280
65280 	 65280    65280    65280    65280    65280    165280   165280
65280 	 65280 	  16777015 65280    65280    165280   65480    165280
16777215 16777215 16777215 16777215 16777215 16777215 16777215 16777215

输出样例1

(5, 3): 16711680

输入样例2

4 5 2
0 0 0 0
0 0 3 0
0 0 0 0
0 5 0 0
0 0 0 0

输出样例2

Not Unique

输入样例3

3 3 5
1 2 3
3 4 5
5 6 7

输出样例3

Not Exist

解题思路

这题就是典型的锻炼我摒弃第一思路就暴力模拟的思维定式的题

  1. 起先真的对角,边,中间各种情况逐一讨论,设定条件,导致代码异常冗长还丑陋并且无法通过
  2. 因为想的全是对某种情况进行条件设定,导致对题干理解马马虎虎
    • 独一无二的颜色:即使满足超过阈值,但是颜色不独一无二不能算,是一个判断条件
    • 差阈值:绝对值也可以
  3. 后面在看别人题解时,虽然也都是分情况讨论角,边,内部,但是给出了更一般的通式,然而自己突然想到如果把矩阵外扩一圈,那么对所有点的讨论就都变成内部点了,尝试之后,果然轻松加愉快。

代码实现

#include 
#include 
#include
using namespace std;

int main() {
	int m, n, k;
	cin >> n >> m >> k;
	vector<vector<int>>v(m + 2, vector<int>(n + 2));
	map<int, int>mp;
    //读入数据,外扩一圈,并且统计各颜色数量,方便后面处理
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
		{
			cin >> v[i][j];
			mp[v[i][j]]++;
		}

	int count = 0;
	int x, y;
	int ans = 0;
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= n; j++)
		{
            //内部点,8个判断再加上判断是否独一无二
			if ((mp[v[i][j]] == 1) && abs(v[i][j] - v[i][j + 1]) > k &&abs(v[i][j] - v[i][j - 1]) > k &&
				abs(v[i][j] - v[i + 1][j - 1]) > k && abs(v[i][j] - v[i + 1][j + 1]) > k && abs(v[i][j] - v[i + 1][j]) > k &&
				abs(v[i][j] - v[i - 1][j - 1]) > k && abs(v[i][j] - v[i - 1][j + 1]) > k && abs(v[i][j] - v[i - 1][j] > k))
			{
				count++;
				x = i; y = j;
                //不独一无二可以直接返回了
				if (count > 1)
				{
					ans = 2;
					break;
				}
				if (count == 1)		ans = 1;
			}
		}
		if (ans == 2)	break;
	}
	if (ans == 2)   cout << "Not Unique";
	if (ans == 0)   cout << "Not Exist";
	if (ans == 1)	cout << "(" << y << ", " << x << "): " << v[x][y];

	return 0;
}

1070 结绳 (25分)

给定一段一段的绳子,你需要把它们串成一条绳。每次串连的时候,是把两段绳子对折,再套接在一起。这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连。每次串连后,原来两段绳子的长度就会减半。

给定 N 段绳子的长度,你需要找出它们能串成的绳子的最大长度。

输入格式

每个输入包含 1 个测试用例。每个测试用例第 1 行给出正整数 N (2≤N≤10^4);第 2 行给出 N 个正整数,即原始绳段的长度,数字间以空格分隔。所有整数都不超过10^4。

输出格式

在一行中输出能够串成的绳子的最大长度。结果向下取整,即取为不超过最大长度的最近整数。

输入样例

8
10 15 12 3 4 13 1 15

输出样例

14

解题思路

此题属于数学问题,排序,然后两个最小的对折,然后再两个小的对折,很好实现,但是做的时候蒙圈了,忘了两个最小的和的一半肯定还是最小的。。。竟然想着得到新绳子后再排序。。。实在是不应该

代码实现

#include 
#include
#include

using namespace std;

int main() {
	int n;
	cin >> n;
	vector<int>num(n);
	for (int i = 0; i < n; i++)
		cin >> num[i];
	sort(num.begin(), num.end());
	int ans = num[0];
	for (int i = 0; i < n; i++)
		ans = (ans + num[i]) / 2;
	cout << ans;
	return 0;
}

1072 开学寄语 (20分)

本题要求你写个程序帮助这所学校的老师检查所有学生的物品,以助其成大器。

输入格式

输入第一行给出两个正整数 N(≤ 1000)和 M(≤ 6),分别是学生人数和需要被查缴的物品种类数。第二行给出 M 个需要被查缴的物品编号,其中编号为 4 位数字。随后 N 行,每行给出一位学生的姓名缩写(由 1-4 个大写英文字母组成)、个人物品数量 K(0 ≤ K ≤ 10)、以及 K 个物品的编号。

输出格式

顺次检查每个学生携带的物品,如果有需要被查缴的物品存在,则按以下格式输出该生的信息和其需要被查缴的物品的信息(注意行末不得有多余空格):

姓名缩写: 物品编号1 物品编号2 ……

最后一行输出存在问题的学生的总人数和被查缴物品的总数。

输入样例

4 2
2333 6666
CYLL 3 1234 2345 3456
U 4 9966 6666 8888 6666
GG 2 2333 7777
JJ 3 0012 6666 2333

输出样例

U: 6666 6666
GG: 2333
JJ: 6666 2333
3 5

解题思路

此题并不难,属于简单的模拟,但是有一个大坑,之前用map存储违规学生的相关信息,然后一起输出,始终不能通过,但是改成普通的逐一输出,就可以通过。

问题在于,unordered_map并不保证按照插入顺序存储,所以以后尽量不要用unordered_map存储内容进行输出,可以用vector存储结果,值得注意。

代码实现

#include
#include
#include

using namespace std;

int main() {
	int snum, bannum;
	int count = 0;
	cin >> snum >> bannum;
	
	string temp;
    map<string,int>mp;
	for (int i = 0; i < bannum; i++)
	{
		cin >> temp;
		mp[temp] = 1;
	}
	string name;
	int kindnum;
	string kind;

	int countman = 0;
	for (int i = 0; i < snum; i++)
	{
		int flag = 0;
		cin >> name >> kindnum;
		for (int j = 0; j < kindnum; j++)
		{
			cin >> kind;
			if (mp[kind] == 1)
			{
				flag ++;
				count++;
				if (flag == 1)
				{
					cout << name << ":"<< " " << kind;
					countman++;
				}
				else cout << " " << kind;
			}
		}
		if(flag)
		cout << endl;
	}
	cout << countman << " " << count;
	return 0;
}

1073 多选题常见计分法 (20分)

批改多选题是比较麻烦的事情,有很多不同的计分方法。有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到 50% 分数;如果考生选择了任何一个错误的选项,则不能得分。本题就请你写个程序帮助老师批改多选题,并且指出哪道题的哪个选项错的人最多。

输入格式

输入在第一行给出两个正整数 N(≤1000)和 M(≤100),分别是学生人数和多选题的个数。随后 M 行,每行顺次给出一道题的满分值(不超过 5 的正整数)、选项个数(不少于 2 且不超过 5 的正整数)、正确选项个数(不超过选项个数的正整数)、所有正确选项。注意每题的选项从小写英文字母 a 开始顺次排列。各项间以 1 个空格分隔。最后 N 行,每行给出一个学生的答题情况,其每题答案格式为 (选中的选项个数 选项1 ……),按题目顺序给出。注意:题目保证学生的答题情况是合法的,即不存在选中的选项数超过实际选项数的情况。

输出格式

按照输入的顺序给出每个学生的得分,每个分数占一行,输出小数点后 1 位。最后输出错得最多的题目选项的信息,格式为:错误次数 题目编号(题目按照输入的顺序从1开始编号)-选项号。如果有并列,则每行一个选项,按题目编号递增顺序输出;再并列则按选项号递增顺序输出。行首尾不得有多余空格。如果所有题目都没有人错,则在最后一行输出 Too simple

输入样例1

3 4 
3 4 2 a c
2 5 1 b
5 3 2 b c
1 5 4 a b d e
(2 a c) (3 b d e) (2 a c) (3 a b e)
(2 a c) (1 b) (2 a b) (4 a b d e)
(2 b d) (1 e) (1 c) (4 a b c d)

输出样例1

3.5
6.0
2.5
2 2-e
2 3-a
2 3-b

输入样例2

2 2 
3 4 2 a c
2 5 1 b
(2 a c) (1 b)
(2 a c) (1 b)

输出样例2

5.0
5.0
Too simple

解题思路

这题是真的太太太长了。。。这是乙级目前我码过最长的题了。。。见过大佬80多行搞定的。。。但我实在不想看了。。。这应该属于模拟里最磨叽的题了。。。哪天闲的蛋疼可以拿来做做。。。感觉这题会做的话所有模拟都不在话下了。。。

代码实现

#include
#include
#include
using namespace std;
struct sub {
	int grade;
    int all_num;
    int correct_num;
	string correct;
};
struct stu {vector<string> ans;};
struct true_ans {int count[5] = {0};};
struct false_ans {int count[5] = {0};};
int main()
{
	int student_num, subject_num;
	cin >> student_num >> subject_num;

	vector<sub>subject_all;
	vector<true_ans>look(subject_num);
	vector<false_ans>subject_count(subject_num);
	//读入正确答案,存储在subject_all中
	for (int i = 0; i < subject_num; i++)
	{
		sub temp_sub;
		cin >> temp_sub.grade >> temp_sub.all_num >> temp_sub.correct_num;
		char temp_char;

		for (int j = 0; j < temp_sub.correct_num; j++)
		{
			cin >> temp_char;
            //读取每个答案应该出现几次
			look[i].count[temp_char - 'a'] = student_num;
			temp_sub.correct.push_back(temp_char);
		}
		subject_all.push_back(temp_sub);
	}

	vector<stu>st(student_num);
	getchar();
	vector<double>student_grade;

	//读取学生的作答,存储在st中
	for (int i = 0; i < student_num; i++)
	{
		string str;
		getline(cin, str);

		string temp_ans;
		//读取学生作答,去掉括号后存储
		for (int j = 0; j < str.size(); j++)
		{
			if (str[j] == ')')
			{
				st[i].ans.push_back(temp_ans);
				temp_ans.clear();
			}
			if (isalpha(str[j]))
				temp_ans.push_back(str[j]);
		}
		//读取实际每个答案出现的次数
		for (int j = 0; j < subject_num; j++)
			for (int k = 0; k < st[i].ans[j].size(); k++)
				subject_count[j].count[st[i].ans[j][k] - 'a']++;

		double sum = 0;
        //统计分数
		for (int j = 0; j < subject_num; j++)
		{
            //满分
			if (st[i].ans[j] == subject_all[j].correct)
				sum += static_cast<double>(subject_all[j].grade);
            //半分情况,即是正确答案的子集,每一个字符在答案中都有,并且没有答案中没有的字符
			else if (st[i].ans[j].size() < subject_all[j].correct.size())
			{
				int final_half = 1;
				for (int k = 0; k < st[i].ans[j].size(); k++)
				{
					int half_grade = 0;
					for (int p = 0; p < subject_all[j].correct.size(); p++)
						if (st[i].ans[j][k] == subject_all[j].correct[p])
						{
							half_grade = 1;
							break;
						}
					if (half_grade == 0)
					{
						final_half = 0;
						break;
					}	
				}
				if (final_half) sum += 0.5*static_cast<double>(subject_all[j].grade);
			}
		}
		student_grade.push_back(sum);	
	}
	
		for (auto x : student_grade)
			printf("%.1f\n", x);
		//做差即为出错次数,包括漏选和错选,并寻找最大值
		int max = 0;
		for (int i = 0; i < subject_count.size(); i++)
			for (int j = 0; j < 5; j++)
			{
				subject_count[i].count[j] = abs(subject_count[i].count[j] - look[i].count[j]);
				if (subject_count[i].count[j] > max)    max = subject_count[i].count[j];
			}

		if (max == 0)		cout << "Too simple";
		else
			for (int i = 0; i < subject_count.size(); i++)
				for (int j = 0; j < 5; j++)
					if (subject_count[i].count[j] == max)
					{
						char c = j + 'a';
						cout << max << " " << i + 1 << "-" << c << endl;
					}

		return 0;
}

1074 宇宙无敌加法器 (20分)

地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的。而在 PAT 星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”。每个 PAT 星人都必须熟记各位数字的进制表,例如“……0527”就表示最低位是 7 进制数、第 2 位是 2 进制数、第 3 位是 5 进制数、第 4 位是 10 进制数,等等。每一位的进制 d 或者是 0(表示十进制)、或者是 [2,9] 区间内的整数。理论上这个进制表应该包含无穷多位数字,但从实际应用出发,PAT 星人通常只需要记住前 20 位就够用了,以后各位默认为 10 进制。

在这样的数字系统中,即使是简单的加法运算也变得不简单。例如对应进制表“0527”,该如何计算“6203 + 415”呢?我们得首先计算最低位:3 + 5 = 8;因为最低位是 7 进制的,所以我们得到 1 和 1 个进位。第 2 位是:0 + 1 + 1(进位)= 2;因为此位是 2 进制的,所以我们得到 0 和 1 个进位。第 3 位是:2 + 4 + 1(进位)= 7;因为此位是 5 进制的,所以我们得到 2 和 1 个进位。第 4 位是:6 + 1(进位)= 7;因为此位是 10 进制的,所以我们就得到 7。最后我们得到:6203 + 415 = 7201。

输入格式

输入首先在第一行给出一个 N 位的进制表(0 < N ≤ 20),以回车结束。 随后两行,每行给出一个不超过 N 位的非负的 PAT 数。

输出格式

在一行中输出两个 PAT 数之和。

输入样例

30527
06203
415

输出样例

7201

解题思路

代码实现并无难度,只是要注意几个坑点

  • 进制为0,要转换为10
  • 最后相加的结果可能有一位进位
  • 测试点5,最后的结果为0时,只可以输出0,输入为0000和000这种情况同样也要输出0

代码实现

#include 
#include
#include
#include
using namespace std;

char add(char a, char b, char d,int & index)
{
	int aa = a - '0';
	int bb = b - '0';
	int dd = d - '0';
	if (dd == 0)	dd = 10;

	int sum = aa + bb+index;
	int temp = sum;
	int count = 0;
	while (temp >= dd)
	{
		temp -=dd;
		count++;
	}
	int ans;
	ans = temp;
	index = count;
	char anschar;
	anschar = ans + '0';
	
	return anschar;
}
int main() {
	string tab;
	string a, b;
	cin >> tab >> a >> b;

	reverse(a.begin(), a.end());
	reverse(b.begin(), b.end());
	reverse(tab.begin(), tab.end());
	while (a.size() < b.size())         a.push_back('0');
	while (b.size() < a.size())		    b.push_back('0');
	string ans;
	int adnum = 0;
	for (int i = 0; i <b.size(); i++)	ans.push_back(add(a[i], b[i], tab[i], adnum));
	if (adnum == 1)             		ans.push_back('1');
	reverse(ans.begin(), ans.end());
    
	int flag = 0;
	for (int i = 0; i < ans.size(); i++)
	{
		if (flag == 0 && ans[i] == '0')			continue;
		else if (ans[i] != 0)
		{
			flag = 1; 
            cout << ans[i];
		}
		else if (flag == 1 && ans[i] == '0')	cout << 0;
	}
	if (flag == 0)		cout << 0;
	return 0;
}

1075 链表元素分类 (25分)

给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而 [0, K] 区间内的元素都排在大于 K 的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为 18→7→-4→0→5→-6→10→11→-2,K 为 10,则输出应该为 -4→-6→-2→7→0→5→10→18→11。

输入格式

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

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

Address Data Next

其中 Address 是结点地址;Data 是该结点保存的数据,为 [−105,105] 区间内的整数;Next 是下一结点的地址。题目保证给出的链表不为空。

输出格式

对每个测试用例,按链表从头到尾的顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。

输入样例

00100 9 10
23333 10 27777
00000 0 99999
00100 18 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 5 68237
27777 11 48652
12309 7 33218

输出样例

33218 -4 68237
68237 -6 48652
48652 -2 12309
12309 7 00000
00000 0 99999
99999 5 23333
23333 10 00100
00100 18 27777
27777 11 -1

解题思路

  1. 结点保存值和地址,用数组的下标表示名字
  2. 既然是分类,那么就创建保存结点名字的三个vector,符合条件的就插进去
  3. 最后按格式输出,注意,每个元素的next地址存储在下一个

总结:感觉没那么难,可能自己思维混乱了

代码实现

#include
#include

using namespace std;

struct node {
	int num;
	int next;
};
int main()
{
	int firstname, nodenum, k;
	cin >> firstname >> nodenum >> k;
	int tempname, tempnum,tempnext;
	vector<node>given(100000);
    //读取数据
	for (int i = 0; i < nodenum; i++)
	{
		cin >> tempname >> tempnum >> tempnext;
		given[tempname].num = tempnum;
		given[tempname].next = tempnext;
	}
	vector<int>ans[3];
    //按要求分类
	while (firstname != -1)
	{
		if (given[firstname].num < 0)
			ans[0].push_back(firstname);
		else if (given[firstname].num <= k)
			ans[1].push_back(firstname);
		else ans[2].push_back(firstname);
		firstname = given[firstname].next;
	}
    //格式化输出
    int flag = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < ans[i].size(); j++) {
            if (flag == 0) {
                printf("%05d %d ", ans[i][j], given[ans[i][j]].num);
                flag = 1;
            } else {
                printf("%05d\n%05d %d ", ans[i][j], ans[i][j], given[ans[i][j]].num);
            }
        }
    }
    printf("-1");
    return 0;
}

1079 延迟的回文数 (20分)

给定一个 k+1 位的正整数 N,如果首位不为0,并且前后对称,N 被称为一个回文数,零也被定义为一个回文数。

非回文数也可以通过一系列操作变出回文数。首先将该数字逆转,再将逆转数与该数相加,如果和还不是一个回文数,就重复这个逆转再相加的操作,直到一个回文数出现。如果一个非回文数可以变出回文数,就称这个数为延迟的回文数

给定任意一个正整数,本题要求你找到其变出的那个回文数。

输入格式

输入在一行中给出一个不超过1000位的正整数。

输出格式

  • 对给定的整数,一行一行输出其变出回文数的过程。每行格式如下

    A + B = C
    

    其中 A 是原始的数字,BA 的逆转数,C 是它们的和。A 从输入的整数开始。重复操作直到 C 在 10 步以内变成回文数,这时在一行中输出 C is a palindromic number.;或者如果 10 步都没能得到回文数,最后就在一行中输出 Not found in 10 iterations.

输入样例1

97152

输出样例1

97152 + 25179 = 122331
122331 + 133221 = 255552
255552 is a palindromic number.

输入样例2

196

输出样例2

196 + 691 = 887
887 + 788 = 1675
1675 + 5761 = 7436
7436 + 6347 = 13783
13783 + 38731 = 52514
52514 + 41525 = 94039
94039 + 93049 = 187088
187088 + 880781 = 1067869
1067869 + 9687601 = 10755470
10755470 + 07455701 = 18211171
Not found in 10 iterations.

解题思路

此题主体部分只要按照要求模拟即可,唯独要注意一个问题,就是给定数字的位数很大,1000位,而long long 最多只能19位,那么测试用例一定会运行出错,多以此题更核心的部分是考察大数加法

代码实现

#include
#include
#include
#include

using namespace std;

int Yes(string num)
{
	if (!num[0])	return 1;
	else {
		string renum;
		renum = num;
		reverse(num.begin(), num.end());
		if (num == renum)
			return 1;
		else return 0;
	}
}
//经典大数加法函数
string BigAdd(string num1,string num2)
{
	string ans;
	int extra = 0;
	int tempsum;
	reverse(num1.begin(), num1.end());
	reverse(num2.begin(), num2.end());
	for (int i = 0; i < num1.size(); i++)
	{
		tempsum = num1[i]-'0'+num2[i]-'0'+extra;
		if (tempsum >= 10)
		{
			ans.push_back(tempsum % 10+'0');
			extra = 1;
		}
		else
		{
			ans.push_back(tempsum +'0');
			extra = 0;
		}
	}
	if (extra)	ans.push_back('1');
	reverse(ans.begin(), ans.end());
	return ans;
}
int main()
{
	string num;
	cin >> num;

	if (num[0] == '0')
		cout << "0 is a palindromic number.";
	else {
		int count = 0;
		while (count <= 9&&!Yes(num))
		{
			string renum;
			string ans;
			renum = num;
			reverse(num.begin(), num.end());
			ans = BigAdd(num, renum);
			cout << renum << " + " << num << " = " << ans << endl;
			num = ans;
			count++;
		}
		if (Yes(num))
			cout << num << " is a palindromic number.";
		else cout << "Not found in 10 iterations.";
	}
	return 0;
}

1081 检验密码 (15分)

本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能。该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母、数字和小数点 .,还必须既有字母也有数字。

输入格式

输入第一行给出一个正整数 N(≤ 100),随后 N 行,每行给出一个用户设置的密码,为不超过 80 个字符的非空字符串,以回车结束。

输出格式

对每个用户的密码,在一行中输出系统反馈信息,分以下5种:

  • 如果密码合法,输出Your password is wan mei.
  • 如果密码太短,不论合法与否,都输出Your password is tai duan le.
  • 如果密码长度合法,但存在不合法字符,则输出Your password is tai luan le.
  • 如果密码长度合法,但只有字母没有数字,则输出Your password needs shu zi.
  • 如果密码长度合法,但只有数字没有字母,则输出Your password needs zi mu.

输入样例

5
123s
zheshi.wodepw
1234.5678
WanMei23333
pass*word.6

输出样例

Your password is tai duan le.
Your password needs shu zi.
Your password needs zi mu.
Your password is wan mei.
Your password is tai luan le.

解题思路

此题代码部分并无难度,仅有一处坑,即因为cin是以空格为结尾的, 但是输入的字符串中有可能有空格,这样就会造成输入的错误,所以应该考虑使用getline(cin,str)函数读入一整行数据,但是要注意,输入N之后的回车会被getline读入,所以应该在输入数据之前用getchar()函数将N之后的回车读取掉。

代码实现

#include
#include
using namespace std;

void check(string str)
{
	int countnum;
	int countal;
	countnum = countal = 0;
	int tag = 1;
	if (str.size() < 6)
		cout << "Your password is tai duan le." << endl;
	else {
		for (int i = 0; i < str.size(); i++)
		{
			if (!isalpha(str[i]) && !isdigit(str[i]) && str[i] != '.')
			{
				cout << "Your password is tai luan le." << endl;
				tag = 0;
				break;
			}
			if (isalpha(str[i]))
				countal++;
			if (isdigit(str[i]))
				countnum++;
		}
		if(tag)
		{
		if (!countnum&&countal)
			cout << "Your password needs shu zi." << endl;
		else if (!countal&&countnum)
			cout << "Your password needs zi mu." << endl;
		else cout << "Your password is wan mei." << endl;
		}
	}
}
int main()
{
	int n;
	cin >> n;
	string s;
	getchar();
	for (int i = 0; i < n; i++)
	{
		getline(cin,s);
		check(s);
	}
	return 0;
}

1088 三人行 (20分)

本题给定甲、乙、丙三个人的能力值关系为:甲的能力值确定是 2 位正整数;把甲的能力值的 2 个数字调换位置就是乙的能力值;甲乙两人能力差是丙的能力值的 X 倍;乙的能力值是丙的 Y 倍。请你指出谁比你强应“从之”,谁比你弱应“改之”。

输入格式

输入在一行中给出三个数,依次为:M(你自己的能力值)、X 和 Y。三个数字均为不超过 1000 的正整数。

输出格式

  • 在一行中首先输出甲的能力值,随后依次输出甲、乙、丙三人与你的关系:如果其比你强,输出 Cong;平等则输出 Ping;比你弱则输出 Gai。其间以 1 个空格分隔,行首尾不得有多余空格。

    注意:如果解不唯一,则以甲的最大解为准进行判断;如果解不存在,则输出 No Solution

输入样例1

48 3 7

输出样例1

48 Ping Cong Gai

输入样例2

48 11 6

输出样例2

No Solution

解题思路

代码部分没有难度,但是对题意理解需要加强

  1. 因为题中并没有说丙一定是整数,所以要用double存储丙的值
  2. 另外限定甲是二位数,所以可以从甲开始出发进行遍历10-99,

代码实现

#include
#include
#include

using namespace std;

int main()
{
	int Mine, A, B, x, y;
	double C;
	cin >> Mine >> x >> y;
	string tmp;
	int have = 0;

	for (int A = 99; A >= 10; A--)
	{

		tmp = to_string(A);
		reverse(tmp.begin(), tmp.end());
		B = stoi(tmp);

		C = static_cast<double>(B) / y;
		if (abs(A - B) == x * C)
		{
			cout << A;
			if (Mine < A)
				cout << " Cong";
			else if (Mine == A)
				cout << " Ping";
			else if (Mine > A)
				cout << " Gai";
			if (Mine < B)
				cout << " Cong";
			else if (Mine == B)
				cout << " Ping";
			else if (Mine > B)
				cout << " Gai";
			if (Mine < C)
				cout << " Cong";
			else if (Mine == C)
				cout << " Ping";
			else if (Mine > C)
				cout << " Gai";
			have = 1;
			break;
		}
	}
	if (!have) cout << "No Solution";
	return 0;
}

1089 狼人杀-简单版 (20分)

已知 N 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎。要求你找出扮演狼人角色的是哪几号玩家?

输入格式

输入在第一行中给出一个正整数 N(5≤N≤100)。随后 N 行,第 i 行给出第 i 号玩家说的话(1≤iN),即一个玩家编号,用正号表示好人,负号表示狼人。

输出格式

如果有解,在一行中按递增顺序输出 2 个狼人的编号,其间以空格分隔,行首尾不得有多余空格。如果解不唯一,则输出最小序列解 —— 即对于两个序列 A=a[1],…,a[M] 和 B=b[1],…,b[M],若存在 0≤k<M 使得 a[i]=b[i] (ik),且 a[k+1]<b[k+1],则称序列 A 小于序列 B。若无解则输出 No Solution

输入样例1

5
-2
+3
-4
+5
+4

输出样例1

1 4

输入样例2

6
+6
+3
+1
-5
-2
+4

输出样例2

1 5

输入样例2

5
-2
-3
-4
-5
-1

输出样例2

No Solution

解题思路

看上去很麻烦的模拟,只要耐心分析就很简单

  1. 读入数据,然后分别假设1,2是狼人,1,3是狼人,如此反复遍历所有狼人的情况
  2. 最后要判断是不是有两个说谎的,且一个是狼人一个不是狼人
  3. 判断说谎的人,要建立起对应的数组,说谎为-1,没说谎为1,说谎的判断条件就是他说的话和实际不符,即相乘为-1,即为说谎
  4. 判断是不是狼人, 同样建立起数组,除了开始的 i 和 j 是狼人,其他都不是狼人

代码实现

#include
#include
#include

using namespace std;

int main()
{
	int num;;
	cin >> num;

	vector<int>say(num + 1);

	for (int i = 1; i <= num; i++)
		cin >> say[i];

	for (int i = 1; i <= num; i++)
	{
		for (int j = i + 1; j <= num; j++)
		{
			//遍历狼人的所有情况,建立当前状况下,是否是狼人的真实判断数组
			vector<int>kind(num + 1,1);
			kind[i] = kind[j] = -1;
			//建立存放说谎人的数组
			vector<int>lie;
			//如果不符合,值为负
			for (int m = 1; m <= num; m++)
			{
				if (say[m] * kind[abs(say[m])] < 0)
					lie.push_back(m);
			}
			//数量够,且一狼一非狼
			if (lie.size() == 2 && kind[lie[0]] + kind[lie[1]] == 0)
			{
				//输出狼的编号,而不是说谎人的编号
				cout << i << " " << j;
				return 0;
			}
		}
	}
	cout << "No Solution";

	return 0;
}

1090 危险品装箱 (25分)

本题给定一张不相容物品的清单,需要你检查每一张集装箱货品清单,判断它们是否能装在同一只箱子里。

输入格式

输入第一行给出两个正整数:N (≤10^4) 是成对的不相容物品的对数;M (≤100) 是集装箱货品清单的单数。

随后数据分两大块给出。第一块有 N 行,每行给出一对不相容的物品。第二块有 M 行,每行给出一箱货物的清单,格式如下:

K G[1] G[2] ... G[K]

其中 K (≤1000) 是物品件数,G[i] 是物品的编号。简单起见,每件物品用一个 5 位数的编号代表。两个数字之间用空格分隔。

输出格式

对每箱货物清单,判断是否可以安全运输。如果没有不相容物品,则在一行中输出 Yes,否则输出 No

输入样例

6 3
20001 20002
20003 20004
20005 20006
20003 20001
20005 20004
20004 20006
4 00001 20004 00002 20003
5 98823 20002 20003 20006 10010
3 12345 67890 23333

输出样例

No
Yes
Yes

解题思路

本题再度考察了对map的理解,比较全面

  1. 因为每个物品可能对应着多个其他物品,所以考虑用map,并且第二个是映射到vector容器中
  2. 对每箱货物,建立访问数组,将箱子编号访问过的设置为1
  3. 再对每箱货物中的每个货物,将其map中映射的所有元素检查,有访问过的,说明冲突了,输出No,反之Yes

代码实现

#include
#include
#include

using namespace std;

int main()
{
	int qingdanshu, xiangshu;
	cin >> qingdanshu >> xiangshu;
	//用map存储列表,每个元素对应多个
	unordered_map<int, vector<int>> qingdan;
	int temp1, temp2;
	for (int i = 0; i < qingdanshu; i++)
	{
		cin >> temp1 >> temp2;
		qingdan[temp1].push_back(temp2);	
	}

	for (int i = 0; i < xiangshu; i++)
	{
        //对每个箱子单独处理,建立访问数组
		int have[100010] = { 0 };
		int k;
		cin >> k;
        //建立存放每个箱子货物的数组
		vector<int>show(k);
		int flag = 0;
		for (int i = 0; i < k; i++)
		{
			cin >> show[i];
			have[show[i]] = 1;	
		}
		for (int i = 0; i < show.size(); i++)
		{
            //每个货物进行检测,如果有访问过的,说明冲突了
			for (int j = 0; j < qingdan[show[i]].size(); j++)
			{
				if (have[qingdan[show[i]][j]] == 1) flag = 1;
			}
		}
		if (flag) cout << "No" << endl;
		else cout << "Yes" << endl;
	}
	return 0;
}

1093 字符串A+B (20分)

给定两个字符串 AB,本题要求你输出 A+B,即两个字符串的并集。要求先输出 A,再输出 B,但重复的字符必须被剔除

输入格式

输入在两行中分别给出 AB,均为长度不超过 106的、由可见 ASCII 字符 (即码值为32~126)和空格组成的、由回车标识结束的非空字符串。

输出格式

在一行中输出题面要求的 AB 的和。

输入样例

This is a sample test
to show you_How it works

输出样例

This ampletowyu_Hrk

解题思路

此题若是判断字符有无出现过,出现放入记录数组,下一个字符再遍历数组判断有无出现,依次判断所有字符,必然有测试用例超时。题目中提示字符是ASCII码对应字符,则建立长为127的数组进行映射,初始为0,访问一次就变成1,再进行判断并随时输出。

代码实现

#include
#include
using namespace std;
int main()
{
	string a, b;
	getline(cin, a);
	getline(cin, b);
	a = a + b;
	int ans[127] = { 0 };

	for (int i = 0; i < a.size(); i++)
	{
		if (!ans[a[i]])
		{
			cout << a[i];
			ans[a[i]] = 1;
		}
	}
	return 0;
}

1094 谷歌的招聘 (20分)

本题要求你编程解决一个更通用的问题:从任一给定的长度为 L 的数字中,找出最早出现的 K 位连续数字所组成的素数。

输入格式

输入在第一行给出 2 个正整数,分别是 L(不超过 1000 的正整数,为数字长度)和 K(小于 10 的正整数)。接下来一行给出一个长度为 L 的正整数 N。

输出格式

在一行中输出 N 中最早出现的 K 位连续数字所组成的素数。如果这样的素数不存在,则输出 404。注意,原始数字中的前导零也计算在位数之内。例如在 200236 中找 4 位素数,0023 算是解;但第一位 2 不能被当成 0002 输出,因为在原始数字中不存在这个 2 的前导零。

输入样例1

20 5
23654987725541023819

输出样例1

49877

输入样例2

10 3
2468024680

输出样例2

404

解题思路

此题实现思路并无难度,取子串,转数字,判断素数,比较坑的地方在测试点4,题目说了L长度,但是输入却可能大于长度L,导致如果用string里的函数length就会出错,用L即可通过

代码实现

#include
#include
#include
using namespace std;

int Isprime(int n)
{
    //素数对1和0的处理不要忽略
	int ans = 1;
	if (n == 1||n==0)
		ans = 0;
	else
		for (int i = 2; i <= sqrt(n); i++)
		{
			if (n%i == 0)
			{
				ans = 0;
				break;
			}
		}
	return ans;
}
int main()
{
	
	int length, n;
	string str;
	cin >> length >> n >> str;
	int have = 0;
	string answer;

	for (int i = 0; i <= length - n; i++)//测试点4的坑,此处如果不用length,用str.length()就错了,因为输入不一定是length长度的
	{
		string substring = str.substr(i, n);
		int num = stoi(substring);
		if (Isprime(num))
		{
			answer = substring;
			have = 1;
			break;
		}
	}

	if (have)
		cout << answer;
	else cout << 404;

	return 0;
}

1095 解码PAT准考证 (25分)

PAT 准考证号由 4 部分组成:

  • 第 1 位是级别,即 T 代表顶级;A 代表甲级;B 代表乙级;
  • 第 2~4 位是考场编号,范围从 101 到 999;
  • 第 5~10 位是考试日期,格式为年、月、日顺次各占 2 位;
  • 最后 11~13 位是考生编号,范围从 000 到 999。

现给定一系列考生的准考证号和他们的成绩,请你按照要求输出各种统计信息。

输入格式

输入首先在一行中给出两个正整数 N(≤10^4)和 M(≤100),分别为考生人数和统计要求的个数。

接下来 N 行,每行给出一个考生的准考证号和其分数(在区间 [0,100] 内的整数),其间以空格分隔。

考生信息之后,再给出 M 行,每行给出一个统计要求,格式为:类型 指令,其中

  • 类型 为 1 表示要求按分数非升序输出某个指定级别的考生的成绩,对应的 指令 则给出代表指定级别的字母;
  • 类型 为 2 表示要求将某指定考场的考生人数和总分统计输出,对应的 指令 则给出指定考场的编号;
  • 类型 为 3 表示要求将某指定日期的考生人数分考场统计输出,对应的 指令 则给出指定日期,格式与准考证上日期相同。

输出格式

对每项统计要求,首先在一行中输出 Case #: 要求,其中 # 是该项要求的编号,从 1 开始;要求 即复制输入给出的要求。随后输出相应的统计结果:

  • 类型 为 1 的指令,输出格式与输入的考生信息格式相同,即 准考证号 成绩。对于分数并列的考生,按其准考证号的字典序递增输出(题目保证无重复准考证号);
  • 类型 为 2 的指令,按 人数 总分 的格式输出;
  • 类型 为 3 的指令,输出按人数非递增顺序,格式为 考场编号 总人数。若人数并列则按考场编号递增顺序输出。

如果查询结果为空,则输出 NA

输入样例

8 4
B123180908127 99
B102180908003 86
A112180318002 98
T107150310127 62
A107180908108 100
T123180908010 78
B112160918035 88
A107180908021 98
1 A
2 107
3 180908
2 999

输出样例

Case 1: 1 A
A107180908108 100
A107180908021 98
A112180318002 98
Case 2: 2 107
3 260
Case 3: 3 180908
107 2
123 2
102 1
Case 4: 2 999
NA

解题思路

此题即模拟题的综合,并不涉及算法,但有很多基础知识的坑

  1. 以后尽量少用cout输出,减少不必要的超时
  2. printf输出string类型的字符串时,要用.c_str()函数转换
  3. 字符映射数字,要想到map,且unordered_map会更快
  4. 注意向vector中push map类型时,要成对push
  5. sort函数中的cmp函数要会写

代码实现

#include
#include
#include
#include
#include

using namespace std;

struct node {
	string number;
	int grade;
};

bool cmp(const node a, const node b) {
	return a.grade != b.grade ? a.grade > b.grade : a.number < b.number;
}
int main()
{
	int snum, cnum;
	cin >> snum >> cnum;

	vector<node> student;
	node temp;

	for (int i = 0; i < snum; i++)
	{
		cin >> temp.number >> temp.grade;
		student.push_back(temp);
	}
		
	int ckind;
	string com;

	for (int command = 0; command < cnum; command++)
	{
		cin >> ckind >> com;
		if (ckind == 1)
		{
			vector<node>ans;
			int count = 0;
			for (int i = 0; i < snum; i++)
			{
				if (student[i].number[0] == com[0])
				{
					ans.push_back(student[i]);
					count++;
				}
			}
			if (count == 0)
			{	
                	//强烈建议使用printf,并注意string在printf中要利用.c_str()转换
				printf("Case %d: %d %s\n",command+1,ckind,com.c_str()); 
				printf("NA\n");
			}
				
			else {
				printf("Case %d: %d %s\n", command + 1, ckind, com.c_str());
				sort(ans.begin(), ans.end(), cmp);
				for (auto item : ans)
					printf("%s %d\n", item.number.c_str(), item.grade);
			}
		}
		else if (ckind == 2)
		{
			int sum = 0;
			int count = 0;
			for (int i = 0; i < snum; i++)
			{
				if (com == student[i].number.substr(1, 3))
				{
					count++;
					sum += student[i].grade;
				}
			}
			if (count == 0) 
			{
				printf("Case %d: %d %s\n", command + 1, ckind, com.c_str());
				printf("NA\n");
			}	
			else
			{
				printf("Case %d: %d %s\n", command + 1, ckind, com.c_str());
				printf("%d %d\n", count, sum); 
			}
		}
		else {
			unordered_map<string, int>answer;
			vector<node>ans;

			for (int i = 0; i < snum; i++)
				if (com == student[i].number.substr(4, 6))
					answer[student[i].number.substr(1, 3)] ++;
			//注意向vector中push map类型时,要成对push
			for (auto item : answer)	ans.push_back({ item.first,item.second });

			sort(ans.begin(), ans.end(), cmp);

			if (answer.size() == 0)
			{
				printf("Case %d: %d %s\n", command + 1, ckind, com.c_str());
				printf("NA\n");
			}
			else {
				printf("Case %d: %d %s\n", command + 1, ckind, com.c_str());
				for (auto item : ans)
					printf("%s %d\n", item.number.c_str(), item.grade);
			}
		}
	}
	return 0;
}

你可能感兴趣的:(题解)