PTA 集训1 题解
L1-1 新世界
-
题面:
这道超级简单的题目没有任何输入。
你只需要在第一行中输出程序员钦定名言“Hello World”,并且在第二行中输出更新版的“Hello New World”就可以了。完整代码
#include<iostream> using namespace std; int main(){ cout << "Hello World\nHello New World"; return 0; }
L1-2 打折
-
题面:
去商场淘打折商品时,计算打折以后的价钱是件颇费脑子的事情。例如原价 ¥988,标明打 7 折,则折扣价应该是 ¥988 x 70% = ¥691.60。本题就请你写个程序替客户计算折扣价。
-
解题思路:
注意输入为两个整数,做除法时需要除以
10.0才会得到浮点数结果。保留两位小数用 printf 自带的格式化输出
%.2lf即可。完整代码
#include<iostream> using namespace std; int main(){ int a,b; cin>> a>>b; printf("%.2lf",a*b/10.0); return 0; }
L1-3 奇偶分家
-
题面:
给定N个正整数,请统计奇数和偶数各有多少个?
-
解题思路:
一边输入一边统计奇数与偶数的个数,最后直接输出即可
x&1可以得到 x 的末位,等同于x%2完整代码
#include<iostream> using namespace std; int main(){ int n,odd=0,even=0; cin >> n; while(n--){ int x; cin >> x; if(x&1) odd++; else even++; } cout << odd << ' ' << even; return 0; }
L1-4 冠军魔术
-
题面:
以桌面上一根带子为界,当他将纸牌从带子的一边推到另一边时,纸牌会变成硬币;把硬币推回另一边会变成纸牌。
这里我们假设纸牌会变成等量的硬币,而硬币变成纸牌时,纸牌的数量会加倍。那么给定纸牌的初始数量,当他来回推了 N 次(来/回各算一次)后,手里拿的是纸牌还是硬币?数量是多少?
这里假设初始状态下魔术师手里全是纸牌。如果最后魔术师手里是纸牌,输出 0 和纸牌数量;如果是硬币,则输出 1 和硬币数量。数字间须有 1 个空格。题目保证结果数值不超出整型范围(即 2^31 −1)。
-
解题思路:
由于初始为纸牌,且只有从硬币推回纸牌时数量才会翻倍,所以推 n 次的翻倍次数为
n/2,即 n 除以 2 向下取整。那么最后手中的物品数量就为初始数量 * 2^(n/2)。若推的次数为奇数,最后为硬币;反之,最后为纸牌,输出即可。
由于本题保证了数值不超过整形范围,代表翻倍的次数很小,那么我们直接循环 n/2 次每次乘 2 即可。
完整代码
#include<iostream> using namespace std; int main(){ int cnt,n; cin >> cnt >> n; for(int i=1;i<=n/2;i++) cnt*=2; printf("%d %d",n&1,cnt); // n为奇数时为硬币,为硬币时输出1 return 0; }
L1-5 判断题
-
题面:
判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分。
输入在第一行给出两个不超过 100 的正整数 N 和 M,分别是学生人数和判断题数量。第二行给出 M 个不超过 5 的正整数,是每道题的满分值。第三行给出每道题对应的正确答案,0 代表“非”,1 代表“是”。随后 N 行,每行给出一个学生的解答。数字间均以空格分隔。
按照输入的顺序输出每个学生的得分,每个分数占一行。
-
解题思路:
记录下每道题的标准答案和分值。对于每个学生答的每道题,若与标准答案相同,增加其获得的分数。
完整代码
#include<iostream> using namespace std; const int N = 105; bool ans[N]; int score[N]; int main(){ int n,m; cin >> n >> m; for(int i=1;i<=m;i++) cin >> score[i]; //分值 for(int i=1;i<=m;i++) cin >> ans[i]; //标准答案 while(n--){ int res = 0; // 统计该学生的得分 for(int i=1,x;i<=m;i++){ cin >> x; if(x == ans[i]) res += score[i]; // 正确时得分 } cout << res << '\n'; } return 0; }
L1-6 检查密码
-
题面:
本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能。该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母、数字和小数点 .,还必须既有字母也有数字。
如果密码合法,输出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.。注意: 题目保证不存在只有小数点的输入。
输入第一行给出一个正整数 N(≤ 100),随后 N 行,每行给出一个用户设置的密码,为不超过 80 个字符的非空字符串,以回车结束。
-
解题思路:
使用 getline(cin,str) 直接读入一整行的数据。
对于每个密码,遍历字符串中的每一个字符。用两个变量 digital 和 ch 分别标记是否出现了数字和字符。
初始值为 0,只要出现过一个字符,就将对应的变量设置为 1。最后根据两个变量的值分类讨论按题意输出即可。
完整代码
#include<iostream> using namespace std; inline string work(string str){ bool digital=false,ch=false; // 是否有数字、字符 for(auto cur:str){ if(cur>='0'&&cur<='9') digital = true; // 数字 else if(cur>='A'&&cur<='z') ch = true; // 字符 else if(cur != '.') return "Your password is tai luan le."; //非法字符 } if(digital && !ch) return "Your password needs zi mu."; if(!digital && ch) return "Your password needs shu zi."; if(digital && ch) return "Your password is wan mei."; return ""; //既没有数字又没有字符的情况题目没有明确输出,返回一个空吧 } int main(){ int T; cin >> T; getchar(); //注意第一行的 N 后有一个回车,会被 getline() 函数误读入,此处需要额外读掉这个回车 while(T--){ string str; getline(cin,str); // 读入一整行数据 if(str.size()<6) cout << "Your password is tai duan le.\n"; // 长度不够直接输出 else cout << work(str) << '\n'; } return 0; }
L1-7 谷歌的招聘
-
题面:
本题要求你编程解决一个更通用的问题:从任一给定的长度为 L 的数字中,找出最早出现的 K 位连续数字所组成的素数。
输入在第一行给出 2 个正整数,分别是 L(不超过 1000 的正整数,为数字长度)和 K(小于 10 的正整数)。接下来一行给出一个长度为 L 的正整数 N。
在一行中输出 N 中最早出现的 K 位连续数字所组成的素数。如果这样的素数不存在,则输出 404。注意,原始数字中的前导零也计算在位数之内。例如在 200236 中找 4 位素数,0023 算是解;但第一位 2 不能被当成 0002 输出,因为在原始数字中不存在这个 2 的前导零。
-
解题思路:
将这个长为 L 的正整数 当做一个字符串 来读入和处理。
-
方法一: O(L*K)
从左往右枚举每一个位置 i。
每次判断:以 i 为开头,向右共k位 构成的数字,是否为一个素数。若是,则找到了最早出现的素数,直接输出;若遍历完了也没有找到,则输出
404。需要注意的是,输出的数字若不满 k 位,需要左侧补零。
完整代码
#include<iostream> #include<string> #include<math.h> using namespace std; inline bool isPrime(int x) { // 判断是否为素数 for (int i=2,up=sqrt(x); i<=up; ++i) // 只需要算到根号 x 即可 if(x%i==0) return false; return x > 1; // 当 x 为 0 或 1 时为合数 } int main() { int n,k; cin >> n >> k; string str; cin >> str; for(int i=0;i<=n-k;++i){ string s = str.substr(i,k); // 截取以 i 为开头,长为 k 的字符串 if(isPrime(stoi(s))) { cout << s; // 若是素数,输出字符串s (此时若有前导零会一同输出) return 0; // 然后直接返回 } } cout << "404"; // 找不到输出 404 return 0; } -
方法二: O(L)
从左往右先读入 K 位,判断是否为素数。
接下来每次读一位放在最低位,再舍弃最高位,得到下一个需判断的数。
如 123456,先读入 1234 判断是否为素数;
然后读入 5 变成 12345,将最高位舍弃变为 2345,得到了下一个应该判断的数如此遍历完共 L 位,若没有素数再输出 404。
时间复杂度从 O(L*K) 降低为 O(L)
需要注意的是,若输出的数字位数不满 K 位,需要在左侧手动输出前导 0
完整代码
#include<iostream> #include<math.h> using namespace std; int n,k,mod=1; inline bool check(int x){ // 判断素数 for(int i=2,up=sqrt(x);i<=up;i++) if(x%i==0) return false; return x > 1; } inline string change(int x){ // 将数字转化为字符串,位数不够则在左侧补充前导 0 int p[10]={0},len=0; string ans = ""; while(x) p[len++]=x%10,x/=10; for(int i=k-1;~i;i--) ans+=char(p[i])+'0'; return ans; } inline string work(){ for(int i=1;i<=k-1;i++) mod*=10; // 模 mod 即可去掉最高位 int x = 0; // 记录当前数字 for(int i=1;i<=k-1;i++) x = x*10 + (getchar()-'0'); // 先读入 k-1 位 for(int i=k;i<=n;i++){ // 每次先读入下一位,计算出当前的数字,然后判断是否为素数 x %= mod, x = x*10 + (getchar()-'0'); if(check(x)) return change(x); // 若为素数,转化为字符串后返回输出 } return "404"; } int main(){ cin >> n >> k; getchar(); if(n<k){ cout << "404"; return 0;} // 若 K 的长度大于整个给定数字的长度,取不够 K 位,直接输出 404 cout << work(); return 0; }
-
L1-8 阅览室
-
题面:
天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时,管理员输入书号并按下S键,程序开始计时;当读者还书时,管理员输入书号并按下E键,程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时,表示一天工作结束,你的程序应输出当天的读者借书次数和平均阅读时间。
注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有S没有E,或者只有E没有S的纪录,系统应能自动忽略这种无效纪录。另外,题目保证书号是书的唯一标识,同一本书在任何时间区间内只可能被一位读者借阅。
每一天的纪录保证按时间递增的顺序给出。
对每天的纪录,在一行中输出当天的读者借书次数和平均阅读时间(以分钟为单位的精确到个位的整数时间)。
-
解题思路:
每一天的图书借阅情况独立计算,那么我们对于每一天的数据分开处理。
由于题目要求:某本只有 E 没有 S 的结束记录书属于非法记录,要忽略不计。
所以我们需要记录下每本书的借出时间,只有当某本书有借出时间的时候,才能被归还。题目还要求统计每天的 有效借书还书记录的总数,和平均阅读时间。
那么我们在每次还书的时候,统计每本书的被借出时间即可。每次读到编号为 0 的时候,表示这一天结束,此时进行输出即可。
需要注意的是,要求保留至整数位,使用 printf 的格式化输出 %.0lf 而不能直接取整。
完整代码
#include<iostream> using namespace std; const int N = 1e3+5; int last_time[N]; // 记录每本书上次借出的时间,单位为分钟 int main(){ int T; scanf("%d",&T); // 总天数 while(T--){ int id,h,m; char op; scanf("%d %c %d:%d",&id,&op,&h,&m); //读入一条记录 int tol_time = 0,people = 0; // 初始化总借出时间,和总借阅人数 for(int i=0;i<N;i++) last_time[i] = -1; // 由于存在 00:00 的借出时间,这里需要初始化为 -1 来表示这本书没有被借出过 while(id){ // id 不为 0,该天没结束 if(op == 'S') last_time[id] = h*60+m; // 若为借书记录,则将这本书的借出时间设置为第 h*60+m 分钟 else if(last_time[id]!=-1){ // 否则为 op = 'E' 的还书记录,此时需判断这本书处于被借出状态,即 last_time[id]!=-1 tol_time += h*60+m-last_time[id]; // 统计借书时间 people += 1; // 统计借书次数 last_time[id] = -1; } scanf("%d %c %d:%d",&id,&op,&h,&m); } if(!people) printf("0 0\n"); // 没有人借书时,算平均数时除数会为 0,特判单独输出 else printf("%d %.0lf\n",people,1.0*tol_time/people); //注意 cnt_time 和 people 均为 int 类型,做除法时要先乘 1.0 使其变为小数除法,最后 %.0lf 保留至整数位 } return 0; }

浙公网安备 33010602011771号