为了备考PAT考试,近期在刷PAT的习题,从易到难。
什么是PAT:
浙江大学计算机程序设计能力考试(Programming AbilityTest,简称PAT)是由浙江大学计算机科学与技术学院组织的统一考试。旨在培养和展现学生分析问题、解决问题和计算机程序设计的能力,科学评价计算机程序设计人才,并为企业选拔人才提供参考标准。
PAT乙级要求掌握的知识:
1.具备基本的C/C++的代码设计能力,掌握相关开发环境的基本调试技巧;
2.理解并掌握最基本的数据结构,如:线性表、树、图等;
3.理解并熟练编程实现与基本数据结构相关的基础算法,包括递归、排序、查找等;
4.学会分析算法的时间复杂度、空间复杂度和算法稳定性;
5.具备问题抽象和建模的初步能力,并能够用所学方法解决实际问题。
本博客用于记录在做PAT乙级题库的时候的新得。同时对一些题目的思路,不容易一次通过的测试要点也会进行记录
本文已经全部更新完毕
PAT乙级也考完了,附上一个成绩单,还需要继续学习:
博客中涉及的代码均存储在我的GitHub仓库中,如有需要可以直接点击链接查看或下载:
PAT乙级代码(所有代码均AC通过)
目录
- PAT乙级题库
-
- 1001 害死人不偿命的(3n+1)猜想
- 1002 写出这个数
- 1003 我要通过!
- 1004 成绩排名
- 1005 继续(3n+1)猜想
- 1006 换个格式输出整数
- 1007 素数对猜想
- 1008 数组元素循环右移问题
- 1009 说反话
- 1010 一元多项式求导
- 1011 A+B 和 C
- 1012 数字分类
- 1013 数素数
- 1014 福尔摩斯的约会
- 1015 德才论
- 1016 部分A+B
- 1017 A除以B
- 1018 锤子剪刀布
- 1019 数字黑洞
- 1020 月饼
- 1021 个位数统计
- 1022 D进制的A+B
- 1023 组个最小数
- 1024 科学计数法
- 1025 反转链表
- 1026 程序运行时间
- 1027 打印沙漏
- 1028 人口普查
- 1029 旧键盘
- 1030 完美数列
- 1031 查验身份证
- 1032 挖掘机技术哪家强
- 1033 旧键盘打字
- 1034 有理数四则运算
- 1035 插入与归并
- 1036 跟奥巴马一起编程
- 1037 在霍格沃茨找零钱
- 1038 统计同成绩学生
- 1039 到底买不买
- 1040 有几个PAT
- 1041 考试座位号
- 1042 字符统计
- 1043 输出PATest
- 1044 火星数字
- 1045 快速排序
- 1046 划拳
- 1047 编程团体赛
- 1048 数字加密
- 1049 数列的片段和
- 1050 螺旋矩阵
- 1051 复数乘法
- 1052 卖个萌
- 1053 住房空置率
- 1054 求平均值
- 1055 集体照
- 1056 组合数的和
- 1057 数零壹
- 1058 选择题
- 1059 C语言竞赛
- 1060 爱丁顿数
- 1061 判断题
- 1062 最简分数
- 1063 计算谱半径
- 1064 朋友数
- 1065 单身狗
- 1066 图像过滤
- 1067 试密码
- 1068 万绿丛中一点红
- 1069 微博转发抽奖
- 1070 结绳
- 1071 小赌怡情
- 1072 开学寄语
- 1073 多选题常见计分法
- 1074 宇宙无敌加法器
- 1075 链表元素分类
- 1076 Wifi密码
- 1077 互评成绩计算
- 1078 字符串压缩与解压
- 1079 延迟的回文数
- 1080 MOOC期终成绩
- 1081 检查密码
- 1082 射击比赛
- 1083 是否存在相等的差
- 1084 外观数列
- 1085 PAT单位排行
- 1086 就不告诉你
- 1087 有多少不同的值
- 1088 三人行
- 1089 狼人杀-简单版
- 1090 危险品装箱
- 1091 N-自守数
- 1092 最好吃的月饼
- 1093 字符串A+B
- 1094 谷歌的招聘
- 1095 解码PAT准考证
PAT乙级题库
1001 害死人不偿命的(3n+1)猜想
思路:这个题很简单,通过对n简单的循环与判断即可实现
1002 写出这个数
题目:写出这个数
思路:需要将正整数存在字符串中,然后将每个字符转换为数字进行加和,最后将求和的数字中各个位数字取出来,按照顺序输出对应拼音即可
代码:写出这个数
1003 我要通过!
题目:我要通过!
思路:需要定义一个函数判断什么样的字符串可以通过,关键在于对第三个条件,如何转化为一个表达式是关键
int judge(string s)//在这里我定义了一个返回值为int类型的函数用于判断字符串s是否满足要求
{
//定义int类型返回值,而不是bool类型,目的是调试的时候根据不同的返回值判断是哪种情况
//规定返回值0是答案正确,大于0是答案错误
int len = s.length();//读取字符串的长度
int p = s.find("P");//找到字符串中第一个出现的P字母
int t = s.find("T");//找到字符串第一个出现的T字母
if (t-p<2)return 1;//若P在T后,或者PT间隔中没有字母,返回1,例如APT,或者ATP这种都会返回1
for (int i = p + 1; i < t; i++)//查找PT中间的字符
{
if (s[i] != 'A')return 2;//中间字符如果有不是A的,返回2,例如PAACT这种
}
//根据题目要求:如果 aPbTc 是正确的,那么 aPbATca 也是正确的,翻译一下就是PT中每增加一个A,字符最后需要增加P前相同的字符,因此P前的字符数*PT中间的字符数=T后面的字符数
//例如AAPAATAAAA,P前2个,PT中间2个,T后面4个
if (p*(t-p-1) != len - t - 1)return 3;//这是对上一步的判断,不满足这个结果的话就返回3
for (int i = 0; i < p; i++)
{
if (s[i] != 'A')return 4;//判断P前的字符是不是都是A,如果不是返回4
}
for (int i = t+1; i < len; i++)//判断P后面的字符是不是都是A,如果不是返回5
{
if (s[i] != 'A')return 5;
}
return 0;//只有满足上述所有条件才能返回0,得到答案正确
}
代码:我要通过!
1004 成绩排名
题目:成绩排名
思路:本题较简单,定义结构数组,使用冒泡排序即可(或者使用算法里的sort函数)
代码:成绩排名
1005 继续(3n+1)猜想
题目:继续(3n+1)猜想
思路:将给定数组中的每一个数的计算数列记录下来,然后对这一系列数组进行消除处理,即:将每个数列的第一个数,在其他数列中(从第二位开始)查找,如果在其他数列中找到了,则证明其他数列包含这个数列,清空自己的数组数列,循环执行,最后留下的就是互相不包含的数组。
//关键的查找循环体,其中cal是一个二维的vecotor,存放每一个数的计算数列
vector<int>::iterator it;
for (int i = 0; i < K; i++)//对每一个数列与其他数列比较,如果其第一个元素存在其他数列中,清空该数列,并追加首位为0与次位的0
{
for (int j = 0; j < K; j++)
{
it = find(cal[j].begin() + 1, cal[j].end(), cal[i][0]);//从i+1行的第二个元素开始找
if (it != cal[j].end())//如果在j中找到,清除自己,并追加0以便之后别的数组查找
{
cal[i].clear();
cal[i].push_back(0);
cal[i].push_back(0);
break;
}
}
}
查找完后余下一堆[0,0]的数组以及相互包含的数组,取出数组的首位降序输出即可
代码:继续(3n+1)猜想
1006 换个格式输出整数
题目:换个格式输出整数
思路:本题较简单,读取出百位,十位,个位后按照要求输出即可。
用 12…n 来表示不为零的个位数字 n(<10)
注意这句话的意思是从1开始循环输出,例如n是2,输出12,n是3,输出123,n是5输出12345
代码:换个格式输出整数
1007 素数对猜想
题目:素数对猜想
思路:本题难度不大,关键在于判断素数的程序运算效率要高,不然会出现运行超时
bool judge(int n)
{
if (n == 2) return true;//2是素数
if (n % 2 == 0)return false;//其他偶数都不是素数
for (int i = 3; i <= sqrt(n); i+=2)//从3开始除,步长为2,判断速度加快一倍
{
if (n%i == 0)return false;
}
return true;
}
代码:素数对猜想
1008 数组元素循环右移问题
题目:数组元素循环右移问题
思路:本题难度不大,要求不借助其他数组,可以使用deque队列,两端进行删除添加效率更高
代码:数组元素循环右移问题
1009 说反话
题目:说反话
思路:使用队列往前段循环输入字符串,然后顺序输出即可
代码:说反话
1010 一元多项式求导
题目:一元多项式求导
思路:定义两个数组,一个用于存放系数,一个存放指数,然后对系数乘以指数即为求导后的系数(分常数项与非常数项)
代码:一元多项式求导
1011 A+B 和 C
题目:A+B 和 C
思路:此题问题很简单,但是要注意的是本题要求计算的整数范围为[-231,231],因此A+B的范围为[-232,232],已经超出C++中int数据类型的范围,因此如果使用int类型会有一些测试用例无法通过,应该使用长整型变量
C++中long与int的范围一样,使用long long类型
代码:A+B 和 C
1012 数字分类
题目:数字分类
思路:此题较简单,使用vector组对数字进行分类储存即可
代码:数字分类
1013 数素数
题目:数素数
思路:此题较简单,通过动态数组存储数据,注意判断素数的程序尽量高效,详情参见1007题。
代码:数素数
1014 福尔摩斯的约会
题目:福尔摩斯的约会
思路:此题的主要陷阱在于判断的字符必须是范围内的数组数据,不属于范围内的字符相同是无效的。
//范围内的数组数据
const string code[7] = {
"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN" };
char t[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N' };
前两个字符串第一个相同的字符必须是A-G(代表1-7),第二个相同的字符必须是0-9或者A-N(代表0-23)后两个字符串第一个相同的字符必须是字母,找到相同元素中的字符位置就解决问题了
//使用count来记录判断到第几个数组,count=0表示还没找到第一个相同的A-G间的字符
//count=1表示已经找到了存放星期的字符,要找存放小时的字符了
for (int i = 0; i < a.size()||i<b.size(); i++)
{
if (count == 0 && a[i] == b[i] && (a[i] >= 'A'&&a[i] <= 'G'))//存放星期
{
week = a[i];
count++;
continue;
}
if (count == 1 && a[i] == b[i] && ((a[i] >= 'A'&&a[i] <= 'N') || (a[i] >= '0'&&a[i] <= '9')))//存放小时
//必须保证相同的字符是t数组中的
{
hour = a[i];
break;
}
}
代码:福尔摩斯的约会
1015 德才论
题目:德才论
思路:此题有三个关键点
- 根据题干对学生正确的分类
根据题目要求划分人物:(注意是用if-else if的判断,前面判断不通过才进入后面的判断)
德 >= H && 才 >= H 为第一类人“才德全尽”
德 >= H && 才 >= L && 才< H 为第二类人“德胜才”,才分不到德分到
德 < H && 德 >= L && 才 < H && 才 >= L && 德 >= 才为第三类人,“才德兼亡”但尚有“德胜才”
德 >= L && 才 >= L为第四类人,只达到最低线要求
- 使用中的sort函数加快排序的速度
我们知道在库中有sort函数,sort函数有个参数(数组起始位置,数组终止位置),这是默认升序排列,如果要自己添加排序顺序可以加入第三个参数,排序方法。
//定义的排序方法函数
bool compare(data a, data b)//sort排序比较的第三个参数,true为排序前,false为后
{
return a>b;
}
//调用方法
sort(data.begin(), data.end(), compare);
排序方法是bool类型的函数
- 使用scanf函数而不是cin函数加快读取数据的速度(据说前者的速度是后者的十倍以上)
此题由于数据量很大,使用cin读取数据会运行时间超出规定范围,需要使用scanf函数
scanf("%d %d %d", &temp.id, &temp.virtue, &temp.gift);
代码:德才论
1016 部分A+B
题目:部分A+B
思路:此题比较简单,循环读取字符串中的每个字符,遇到相同的字符计数,根据计数返回一个整数类型的PA即可
代码:部分A+B
1017 A除以B
题目:A除以B
思路:由于题目要求计算不超过1000位的正整数,不能使用计算机的整数数据类型计算(范围不够),因此本题的解决方式是将人用纸笔计算除法的过程转换为程序,即:从最高位开始除,将余数记到下一位的进位当中,关键函数代码如下:
int div(char *a, int b)//a是被除数,b是除数,div是余数
//使用指针变量可以返回计算的除数的字符数组,同时还返回了余数
{
int i = 0;
int div = 0;
int temp;
while (a[i] != '\0')
{
//将每一位字符转化为数字加上上一位的余数*10然后做除法
temp = (a[i] - '0' + div*10) % b;//记录余数,如果前面有余数,需要加上借位
a[i] = '0' + (a[i] - '0'+div*10) / b;//记录除数
div = temp;
i++;
}
return div;
}
另外,还要注意的是,还要注意结果为0,或者首位为0的特殊情况
代码:A除以B
1018 锤子剪刀布
题目:锤子剪刀布
思路:本题比较简单,定义函数对胜负状况进行判断,根据胜负状况记录谁通过什么策略获胜,最后按照要求输出结果即可
代码:锤子剪刀布
1019 数字黑洞
题目:数字黑洞
思路:本题比较简单,定义一个函数对一个四位数进行到达数字黑洞的演算,将原数字每一位取出,存放到vector数组中,然后使用sort函数排序,将排序结构表示的大数与小数存储下来,返回相减结果,同时输出结果即可
代码:数字黑洞
1020 月饼
题目:月饼
思路:本题是一个背包问题,需要先建立结构体变量,用于记录月饼的库存,总价与单价,根据单价最大原则进行取,放入背包
struct mooncake
{
double stocks;//月饼库存
double total;//月饼总价
double price;//月饼单价
};
排序的时候使用自定义的比较规则调用sort函数,与1015题类似:
//比较原则为单价大,排序考前
bool compare(mooncake a, mooncake b)//对sort函数定义比较方法
{
return a.price > b.price;
}
注意这个题有几个测试点有陷阱
1:只说了市场最大需求量为整数D,但是库存售价只说是正数(存在小数的情况,因此需要使用double数据类型)
2:如果需求大于库存总量,只能输出所有的库存的总价,因为无法提供更多的月饼
代码:月饼
1021 个位数统计
题目:个位数统计
思路:这个题比较简单,将数字读取到字符串中,然后对字符串进行从0-9的字符查找计数即可,最后将计数不为零的结果输出即可
代码:个位数统计
1022 D进制的A+B
题目:D进制的A+B
思路:本题难度不大,主要是需要定义一个函数对十进制数进行转化,对十进制数转化的方法为对其进行求余运算,例如将56转化为7进制数,计算方法为:
56/7=8···0
8/7=1···1
1/7=0···1
直到除数结果为0,逆序记录余数,110即为56的7进制数
程序如下:
string tentoD(int number, int D)//根据进制数D将10进制数字转化为字符串
{
if (number == 0) return "0";//本题有一个注意点是A,B都为0的情况要予以考虑,有一个测试用例就是对0的结果的输出
vector<int> s;
string n;
while (number != 0)//进制数的转化,记录余数
{
s.push_back(number%D);
number /= D;
}
int temp;
while (!s.empty())
{
temp = *(s.end() - 1);//取最后一位数字,逆序即为转换后的字符串
n +='0' + temp;
s.pop_back();
}
return n;
}
定义好函数直接计算加和结果通过函数转化一下进制数即可。
注意本题有一个测试用例是A,B都为0,需要在函数中对0的情况加以考虑
另外,附上将D进制字符串转化为10进制数的方法:
//本函数需要include<stringstream>与<cmath>
int Dtoten(string a, int D)//字符串a是D进制数,需要将其转化为10进制数
{
int number=0;
for (int i = 0; i < a.size(); i++)
{
number += (a[i]-'0') * pow(D, a.size() - 1 - i);
}
return number;
}
代码:D进制的A+B
1023 组个最小数
题目:组个最小数
思路:本题比较简单,将0-9的个数存在一个长度为10的数组,先对下标为1-9的数组寻找,找到第一个个数不为0的数字,将其输出作为首位,同时对这个下标的数字个数减去1,最后对下标为0-9的数组按照个数输出序号即可
代码:组个最小数
1024 科学计数法
题目:科学计数法
思路:本题难度适中,思路是将字符一个一个读取出来,按照规则进行输出。
- 首先读第一个字符,如果是‘+’则不输出,若是‘-'则输出‘-’。
- 读符号E以前的字符,存在一个字符数组s中
- 读符号E以后的字符,存在一个整型n中
按照规定读好数据以后,就根据n的情况对字符数组表示的数字进行科学计数法的还原
- 当n小于0,则需要将字符的小数点向前移动n位,由于小数点前只有一个数字,因此移动小数点就要在首位数字前增加-n个零同时把原来的小数点删除,在新的第二位增加小数点,例如+1.2E-3,需要在前面增加三个零变为0.0012,关键代码如下:
//s是存储字符数组的deque对象
s.erase(s.begin() + 1);//移除原来第二位的小数点
for (int i = 0; i < -n; i++)
s.push_front('0');//在数组前增加-n个零
s.insert(s.begin() + 1,'.');//在新的字符数组第二位加上小数点
- 当n大于零的时候,需要分两类考虑:
一类是n小于s小数点以后的数字的个数,例如+1.23400E+03,小数点后有5个数字,而n=3,这时只要将小数点后移n位即可。操作如下:
//小数点的位置是s.begin()+1
s.insert(s.begin() + 1 + n + 1, '.');//在小数点后的n+1位置增加一个小数点
s.erase(s.begin() + 1);//删除原来的小数点
- 第二类是n大于等于s小数点以后的数字的个数,例如+1.23400E+06或者+1.23400E+05,这时只要删除原来的小数点,然后在数组最后增加(n-小数点以后的数字的个数)个零,例如+1.23400E+06,n=6,小数点以后有5个数字,只要删除小数点,然后在后面加6-5=1个零变为1234000即可。操作如下:
//number=s.size()-2,是小数点以后的数字的个数
for (int i = 0; i < n - number; i++)
s.push_back('0');//在后面追加n-number个0
s.erase(s.begin() + 1);//移除小数点
代码:科学计数法
1025 反转链表
题目:反转链表
思路:本题难度略高,有两个测试用例不好通过,一是大量的节点数据,查找超时的问题,二是不一定所有的节点都是有效节点,对于这种情况需要删除无效的节点
例如这种:
00100 6 2
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 77777
12309 2 33218
在这个测试用例中,节点5的下个地址是77777,但是并没有节点的地址是77777,因此在链表中5之后的节点就应该要删除,最后的结果应该是2-1-4-3-5
另外本题还需要注意的是:对于最后小于K的节点,是不需要逆序的。例如这个测试用例,最后剩两个节点5,6,个数是小于4的,因此5,6就不需要逆序,最后的结果应该是4-3-2-1-5-6
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
而解决查找速度的问题,本人使用了< algorithm >库中的find_if函数
关于find_if函数,以后如有机会可以详细介绍一下,在这里的关键代码如下:
先定义判断函数:
class judge//对find_if算法的第三个函数参数的定义
{
private:
int address;//judge函数是对节点node a进行判断,判断其地址是不是int address
public:
judge(int add) :address(add){
}//必须对该类进行构造函数的定义,由于在find_if中,第三个参数需要传入一个变量add,需要判断该变量与node中的地址是否相同
bool operator()(node a)//由于find_if第三个参数无法传入自己的node值,因此需要对操作符()进行重新定义,使其可以使用操作符来间接调用自己的值
{
return a.address == address;//判断judge(add),如果a与node a的address相同返回true
}
};
可以直接调用函数查找下一个节点:
vector<node>::iterator it;//先定义指针
it = find_if(L.begin(), L.end(), judge(first));
//找到地址为first的节点
以上是该题的两个难点的解决方法。
最后总结一下整个程序的思路
代码:反转链表
1026 程序运行时间
题目:程序运行时间
思路:本题很简单,只要注意输出的格式,两位不足补0即可,同时注意四舍五入的情况
代码:程序运行时间
1027 打印沙漏
题目:打印沙漏
思路:此题思路比较简单,主要是有一个陷阱比较坑:不需要输出多余的空格!!!
(以后看到格式有问题的情况可以将题目的输出案例复制到记事本上,然后一个一个的对比字符)
题目的思路如下,首先观察沙漏的字符个数与行数的关系:
1行沙漏只有1个字符,2行沙漏有7个字符,3行有17个,4行有31个,可以得到一个公式:n行的沙漏有2n2-1个字符。
这样就可以根据给定的字符个数,找出最大的沙漏的行数。
这里我使用了一个count函数来计算这个行数row
int count(int n)//计算n个字符可组成的最大的沙漏的行数
{
int count = 1;
while (1)
{
if (n >= 2 * count*count - 1)count++;
else return count - 1;
}
}
得到行数以后,我们可以得到最多一行输出的字符的个数为length=2row-1。
对于沙漏的第i行来说,这一行要输出2i-1个字符,为了居中显示,需要在其前面输出一定的空格,空格数应该等于[length-(2i-1)]/2,这样先从i=row输出到i=1,在从i=2输出到i=row即可得到沙漏。
代码:打印沙漏
1028 人口普查
题目:人口普查
思路:此题略有难度,主要思路如下,定义struct结构体,存储每个人的名字与姓名。
struct person
{
char name[6];
int year, month, day;
};
由于有的人的日期显示超过200岁或者大于现在的日期,这些都是不合理的需要筛除。一开始我想的方法是分别将年月日进行对比:
bool judge(int year, int month, int day)//
//先比较年在不在范围
//然后比较两个特殊年份的月在不在范围
//最后比较两个特殊的年份,特殊的月份,日在不在范围
if (year > 1814 && year < 2014) return true;
else if ((year == 1814 && month>9) || (year == 2014 && month < 9))return true;
else if ((year == 1814 && month == 9 && day >= 6) || (year == 2014 && month == 9 && day <= 6))return true;
else return false;
}
后来发现后更好的方法,就是将年份月份日期进行计算,将三个数合为一个数birthday=10000year+100month+day,这样就可以直接进行比较:
struct person//定义新的结构体
{
char name[6];
int birthday;//birthday=10000year+100month+day
};
bool judge(int birthday)
{
if (birthday < 18140906 || birthday>20140906) return false;
else return true;
}
定义好筛除函数以后,首先找到第一个符合日期范围的人,将其同时设置为max与min:
int N;//总共有N行数据
cin >> N;
char name[6];
int year, month, day;//临时存放年月日
int birthday;//通过年月日计算生日的8位数字
person max, min;
int n = 0;//定义n是为了计数,看找到哪一位了
int count = 0;
while (1)
{
scanf("%s %d/%d/%d", name, &year, &month, &day);//读取第一个人的名字
birthday = year * 10000 + month * 100 + day;
n++;
if (judge(birthday))//找到一地个日期合理的人的生日
{
count++;
break;
}
if (n == N)//注意这里!一开始没有写这个if函数,导致始终有一个测试用例运行超时,后来想到是这个while函数无法跳出循环,原因是给出的所有日期都不符合要求范围
{
printf("0");//这种情况需要输出一个0
return 0;
}
}
strcpy(max.name, name);//将其存在max与min中
max.birthday = birthday;
strcpy(min.name, name);
min.birthday = birthday;
一开始这里犯了一个严重的错误,导致有一个测试用例始终显示超时,检查了很久才发现如果所有给的数据都是不符合要求的,这时候要跳出循环并输出0,不能等待一直输入。
for (int i = n; i < N; i++)//找到第一个满足条件的数据后,从后一位开始循环到最后
{
scanf("%s %d/%d/%d", name, &year, &month, &day);
birthday = year * 10000 + month * 100 + day;
if (judge(birthday))//以后一找到一个就计数
{
count++;
}
else continue;//如果没有找到继续循环
if (max.birthday>birthday)//找到满足的数据,将其与max的生日进行对比,如果max生日比这个晚,将max的生日与姓名更改为新的这个人
{
strcpy(max.name, name);
max.birthday = birthday;
}
if (min.birthday<birthday)//同上,看其与min的关系
{
strcpy(min.name, name);
min.birthday = birthday;
}
}
printf("%d %s %s", count, max.name, min.name);
return 0;
代码:人口普查
1029 旧键盘
题目:旧键盘
思路:此题难度适中,思路是将两个句子的每个字符存到两个集合中(相同的元素集合中只能出现一次)
下面是将一个字符存入集合的操作,注意将小写转化为大写
string sen1;
set<char> s1;
char temp;
for (int i = 0; i < sen1.size(); i++)//将sen1中的每一个字符插入集合s1中
{
temp = sen1[i];
if (temp >= 'a'&&temp <= 'z')temp += 'A' - 'a';//如果是小写字母,转化为大写字母
s1.insert(temp);
}
然后对两个集合求差找到那些坏键。
//对比两个集合,将s1中s2有的清除
set<char>::iterator it,p;
for (it = s2.begin(); it != s2.end(); it++)
{
for (p = s1.begin(); p != s1.end(); p++)
{
if (*it == *p)
{
s1.erase(p);
break;
}
}
}
由于题目要求按照输入顺序输出坏键,因此需要在原句子中找到坏键的序号
for (int i = 0; i < sen1.size(); i++)//首先将sen1中所有的小写字母转化为大写字母
{
if (sen1[i] >= 'a'&&sen1[i] <= 'z')sen1[i] += 'A' - 'a';
}
vector<int> number;//存入序号
int tep;
for (it = s1.begin(); it != s1.end(); it++)//对s1中剩下的字符一个个在sen1中查找其顺序(使用find函数),将序号存入number中
{
tep = sen1.find(*it);
number.push_back(tep);
}
最后根据序号的升序顺序输出对应的字符即可。
sort(number.begin(), number.end());
for (int i = 0; i < number.size(); i++)//最后输出这些序号代表的字符
{
cout << sen1[number[i]];
}
return 0;
}
代码:旧键盘
1030 完美数列
题目:完美数列
思路:本题难度略大(一开始的时候想加快速度,定义了一个数,last=max/p,想从最小数循环到last就可以了,减少了几个循环,结果导致最后一个测试用例始终通过不了)
个人猜测最后一个案例可能类似是这样的(就是包括p有很多大数的情况):
6 99999998
1 999999995 999999996 999999997 999999998 999999999
其实抛开这个测试点,整体的思路还是比较清晰的
首先读取所有的数存入数组中,然后使用sort进行排序:
int N;
double p;
double n[100001];
cin >> N >> p;
for (int i = 0; i < N; i++)
{
scanf("%lf", &n[i]);
}
sort(n, n + N);
然后定义一个双循环,从小到大,计算每个数的完美序列的个数,将最大的存起来:
注意内循环查找的时候可以从i+max开始,可以大大减少查找时间(不加的话有一个测试用例会运行超时)
int max = 1;//首先定max=1
for (int i = 0; i < N; i++)//i从0开始循环
{
for (int j = i+max; j < N; j ++)//j从i+max开始判断可以减少很多不必要的判断,大大加快程序的运算速度
{
if (n[j] > p*n[i])//找到第一个比p*n[i]大的数的序号j
{
max = (j - i)>max ? (j - i) : max;//根据j与i的位置,计算这个数列的个数为j-i(因为j的前一个数字才满足完美数列要求,n[j]并不满足)
break;
}
else if(n[j]==p*n[i]||j==N-1)//如果找到的是第一个等于p*n[i],计算数列个数的时候要+1
//当找到最后一个数字都没有满足>=的情况的时候,证明i后面所有的数字都可以组成完美序列,也要停止
{
max = (j - i + 1) > max ? (j - i + 1) : max;
break;
}
}
}
最后输出max即可。
代码:完美数列
1031 查验身份证
题目:查验身份证
思路:这个题难度不高,对每个字符串前17位进行一个判断即可,不满足判断的情况就将其存起来
判断函数如下,注意要考虑检查前17个字符是否全为数字,不满足的需要直接给false
int weight[] = {
7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 };
char M[] = {
'1','0','X','9','8','7','6','5','4','3','2'};
bool judge(string a)//判断一个字符串是否合理
{
int sum = 0;
for (int i = 0; i < 17; i++)
{
if (a[i]<'0' || a[i]>'9') return false;//注意要检查是否前17位全是数字
sum += (a[i] - '0')*weight[i];//对前17位进行加权求和
}
sum = sum % 11;
char last = M[sum];
if (last == a[17])return true;
else return false;
}
代码:查验身份证
1032 挖掘机技术哪家强
题目:挖掘机技术哪家强
思路:这个题目名字起得很风骚,但是题目却很简单,由于题目中学校的序号是从1开始连续编号,只要定义个长度为105的数组,对下标为序号的数字进行求和,然后找到总分最大的数字输出序号与总分即可。
代码:挖掘机技术哪家强
1033 旧键盘打字
题目:旧键盘打字
思路:本题难度不高,但是我却卡了很久(从2018年年末调bug调到了最后一秒,不过好歹也算在2018年的最后调好了(T_T)),最后发现是一个很蠢的失误,是由于在主函数外的函数中一个if后面没有else,导致在一些情况下会没有返回值,导致判断程序出错。这在编译的时候其实就提醒过我这个warning:C4715,但是我没有在意,导致我最后找了很久的错
这告诉我一个道理:
每一个if后都要规范书写加上else,就如同做人一样,不能只考虑if,还要给自己留一个else的后路
话不多说,说一下本题的思路,本题主要就是对第二个字符串如何判断的问题,这里我定义了一个judge判断函数:
bool judge(char s1, char s2)//这个函数用于比对两个字符,判断有无坏键,是否要输出,s1代表坏键
{
if (s1 == s2) return false;//如果相同,代表是坏键,返回false
if (s1 == '+' && (s2 >= 'A'&&s2 <= 'Z'))return false;//虽然不同,但是上档键坏了,所有大写字符不输出
else if (s2 >= 'a'&&s2 <= 'z')//如果是小写字母,要判断他的大写字母是否是坏键
{
if (s2 + 'A' - 'a' == s1) return false;
else return true;//一开始就是这句语句没有写,导致提交的时候三个大的测试点不能通过,以后一定要注意每一个if后面都要加上else
}
else return true;
}
定义好判断函数后,定义双层循环,对第二个字符的每一个字符在第一个字符中循环判断,必须所有的判断结果都为true,最后才有可能输出这个字符
for (int i = 0; i < s2.size(); i++)//对比s2中的每一个字符,判断是否要输出
{
bool temp = true;
for (int j = 0; j < s1.size(); j++)//将s2的字符与s1中每一个字符进行对比
{
temp = temp && judge(s1[j], s2[i]);//将所有逻辑判断结果求“与”,必须所有的是true最后结果才为true
}
if (temp)cout << s2[i];//所有判断都是true,可以输出
}
注意本题还有一个测试
点听说比较坑,就是第一个字符为空的情况(这个我一开始就考虑了,所以没遇到这个测试点不通过),所以要用getline函数读取每一行的数据
代码:旧键盘打字
1034 有理数四则运算
题目:有理数四则运算
思路:本题难度较大,最好使用面向对象的编程方法,思路如下:
- 首先构造一个分数类:
分数类包含的信息有:这个分子原始的分子分母,化简后的整数部分,化简后分子分母,最大公约数,符号信息
class fraction
{
private:
long long a, b;//原始的分子分母
long long mol;//分子
long long den;//分母
long long gcd;//最大公约数
long long integer;//整数部分
bool judge;//记录是否为负数,false为负数
bool legal=true;//判断这个分数是否合法(分母为零不合法)
public:
fraction(){
};
fraction(long long a, long long b) :a(a), b(b){
...};
void set(long long a, long long b){
...}
void print();
fraction plus(fraction a);
fraction sub(fraction a);
fraction mul(fraction a);
fraction div(fraction a);
};
- 定义构造函数与初始化函数:
fraction(long long a, long long b) :a(a), b(b)
{
judge = true;//记录是否为负数
if (a < 0)
{
a = -a;
judge = false;//false代表是负数
}
//一开始使用从2开始循环判断是否能整除来查找最大公约数,后来运行超时,说明该算法不合适
//int g = 1;
//for (int i = 2; i <= a && i <= b; i++)//计算最大公约数
//{
// if ((b%i == 0) && (a%i == 0))g = i;
//}
//后来使用辗转相除法来寻找最大公约数
//先找两个数中的较大的数
long long max = a >= b ? a : b;
long long min = a < b ? a : b;
long long temp;
while (min != 0)//计算max % min =c
//然后max=min,min=c;继续计算,直到min等于0为止,max就是最大公约数
{
temp = max % min;
max = min;
min = temp;
}
gcd = max;
integer = a / b;//记录整数部分
mol = (a-integer * b)/gcd;//记录去掉整数部分的分数部分
den = b / gcd;//记录分母
};
set函数同理,但是set函数需要对分数是否合法判断
if (b == 0)
{
legal = false;
return;
}
- 定义输出这个分数的函数:
void fraction::print()
{
if (!legal)//首先判断是否合法,不合法的话输出Inf
{
cout << "Inf";
return;
}
if (mol == 0 && integer == 0)//如果分子与整数部分都为零输出0
{
cout << 0;
return;
}
if (judge)//如果不是负数不需要加括号
{
if (integer != 0)//如果整数部分不为零需要输出整数部分
{
if (mol == 0)//如果分子为零,输出完整数部分直接结束
{
cout << integer;
return;
}
else//否则需要多输出一个空格
{
cout << integer << ' ';
}
}
//输出分子与分母
cout << mol << '/' << den;
return;
}
else//是负数同上,只是在开始前需要加上'('与'-'号,最后return前需要加')'
{
cout << "(-";
if (integer != 0)
{
if (mol == 0)
{
cout << integer << ')';
return;
}
else
{
cout << integer << ' ';
}
}
cout << mol << '/' << den << ')';
return;
}
}
- 定义加减乘除运算:
加:
fraction fraction::plus(fraction a)
{
fraction temp;
long long mol, den;
mol = this->a * a.b + a.a*this->b;
den = this->b * a.b;
temp.set(mol, den);
return temp;
}
减:
fraction fraction::sub(fraction a)
{
fraction temp;
long long mol, den;
mol = this->a * a.b - a.a*this->b;
den = this->b * a.b;
temp.set(mol, den)