PTA 乙级难点题目列表

  • 1002 写出这个数(字符串)
  • 1003 我要通过!
  • 1004 成绩排名
  • 1005 继续(3n+1)猜想
  • 1008 数组元素循环右移问题
  • 1009 说反话
  • 1010 一元多项式求导
  • 1012 数字分类
  • 1015 德才论
  • 1017 A除以B
  • 1023 组个最小数
  • 1024 科学计数法
  • 1025 反转链表
  • 1026 程序运行时间
  • 1027 打印沙漏
  • 1028 人口普查
  • 1029 旧键盘
  • 1030 完美数列
  • 1033 旧键盘打字
  • 1034 有理数四则运算
  • 1035 插入与归并
  • 1039 到底买不买
  • 1042 字符统计
  • 1044 火星数字
  • 1045 快速排序
  • 1048 数字加密
  • 1049 数列的片段和
  • 1050 螺旋矩阵
  • 1051 复数乘法
  • 1052 卖个萌
  • 1054 求平均值
  • 1055 集体照
  • 1056 组合数的和
  • 1058 选择题
  • 1059 C语言竞赛
  • 1060 爱丁顿数
  • 1064 朋友数
  • 1067 试密码
  • 1068 万绿丛中一点红
  • 1069 微博转发抽奖
  • 1070 结绳
  • 1073 多选题常见计分法
  • 1074 宇宙无敌加法器
  • 1075 链表元素分类
  • 1076 Wifi密码
  • 1080 MOOC期终成绩
  • 1084 外观数列
  • 1085 PAT单位排行
  • 1086 就不告诉你
  • 1089 狼人杀-简单版
  • 1090 危险品装箱
  • 1091 N-自守数
  • 1093 字符串A+B
  • 1095 解码PAT准考证

1002 写出这个数(字符串)

#include<iostream>
using namespace std;
char op[1000];
char ed[10][5]={"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
int boss[100];
int pep=0,j=0;
int main()
{cin>>op; //char数组可以直接输入字符串for(int i=0;op[i]!='\0';i++){pep+=op[i]-'0';}while(pep!=0){boss[j]=pep%10;pep/=10;j++;}j--;for(int i=j;i>=0;i--){cout<<ed[boss[i]];if(i!=0) cout<<' ';}return 0;
}

1003 我要通过!


#include<iostream>
#include<cstring>
using namespace std;
int main()
{int cnt;scanf("%d",&cnt);while(cnt--){char str[1000];cin>>str;int i,state=0,Afront=0,Amiddle=0,Alater=0;for(i=0;i<strlen(str);i++){if(str[i]!='P'&&str[i]!='A'&&str[i]!='T') break;if(str[i]=='P')if(state==0)state=1;elsebreak;if(str[i]=='A')if(state==1)state=2;else if(state==2)Amiddle++;else if(state==0)Afront++;elseAlater++;if(str[i]=='T')if(state==2)state=3;elsebreak;}while(Amiddle--){Alater-=Afront;}if(state!=3||Afront!=Alater||i<strlen(str))cout<<"NO"<<endl;elsecout<<"YES"<<endl;}return 0;
}

分析题目三个通过条件可知:字符串中不能出现P、A、T之外的字符;PT之间只有一个A时(PAT),前后可以加上相同个数的A;PT中每多一个A(>2),后面加上前面个数个A,

统计字符串中P、T前中后A的个数判断即可


1004 成绩排名

#include<iostream>
using namespace std;
typedef struct Student
{char name[100];char number[100];int score;
}student;
student _max={"","",0},_min={"","",100};
int main()
{int cnt;cin>>cnt;while(cnt--){student use;cin>>use.name;cin>>use.number;cin>>use.score;if(use.score>_max.score)_max=use;if(use.score<_min.score)_min=use;}cout<<_max.name<<" "<<_max.number<<endl;cout<<_min.name<<" "<<_min.number;return 0;
}

注意 typedef 的用法,还有 max min new 不可以作为变量名使用,再提醒一下:结构体之间是可以赋值的


1005 继续(3n+1)猜想

#include<iostream>
using namespace std;
int process[4500]; //过程数
int cnt[101]; //当前数
void progress(int x)
{if(process[x]) //避免重复记录return;while(x!=1){if(x%2==0)x/=2;elsex=(x*3+1)/2;process[x]=1;}
}
int main()
{int num,tmp;cin>>num;while(num--){cin>>tmp;cnt[tmp]=1;progress(tmp);}int firstblood=0; //题目要求最后一个数字后没有空格for(int i=100;i>0;i--){if(cnt[i]&&process[i]==0&&firstblood==0){firstblood=1;cout<<i;}else if(cnt[i]&&process[i]==0&&firstblood==1)cout<<" "<<i;}return 0;
}

题目意思是第一个直接执行3n+1猜想,并记录过程中的每个数,注意是过程数;后面的每个数先判断是否已经记录过,记录过就直接跳过(记录过后面算就没意义了),没记录过就执行3n+1猜想并记录过程数


1008 数组元素循环右移问题

#include<iostream>
using namespace std;
int array[105];
int main()
{int num,run;cin>>num>>run;for(int i=1;i<=num;i++)cin>>array[i];int begin=num-run%num+1; //解决数循环移次数的方法for(int i=1;i<=num;i++){if(begin>num) //再打印 begin 之前的begin=1;cout<<array[begin++]; //技巧处,先从 begin 开打印if(i<num)cout<<' ';}return 0;
}

题目本身不难,但要关注本代码的小技巧


1009 说反话

#include<iostream>
using namespace std;
char a[81][81],c;
int length=80,i=0;
int main()
{while((c=getchar())!='\n') //典型的字符串输入{if(c==' '){a[length][i]='\0';length--; //从后往前i=0;continue;}else{a[length][i]=c;i++;}}a[length][i]='\0';for(int j=length;j<=80;j++){if(j==80)cout<<a[j];elsecout<<a[j]<<' ';}return 0;
}

特别注意:输出字符串时,程序读取到 '\0' 后面都不会输出,如 "123\0456",只打印 123


1010 一元多项式求导

#include<iostream>
using namespace std;
int main()
{int a,b,flag=0;while(cin>>a>>b) //典型的数字输入{if(a*b){if(flag)cout<<' ';elseflag=1;cout<<a*b<<' '<<b-1;}}if(!flag) //题目原话 : 注意 “零多项式” 的指数和系数都是 0,但是表示为 0 0cout<<0<<' '<<0;return 0;
}

可以利用规律来相乘和减 1,比如例题中 3 * 4 = 12 ,- 5 * 2 = -10 ,6 * 1 = 6 ,- 2 * 0 = 0,这是对应的系数,4 - 1 = 3 ,2 - 1 = 1 ,1 - 1 = 0,这是指数,最后一个是常数 -2,指数不做运算,这是要注意的

另外本代码的 flag 有两个用处,一是解决题目要求的:结尾不能有多余空格,二是满足题目:“零多项式” 的指数和系数都是 0,但是表示为 0 0


1012 数字分类


#include<iostream>
#include <iomanip> //此头文件也别忘了
using namespace std;
int main()
{int cnt,A=0,B=-1,C=0,E=0,B2=0,B3=0;double D=0,D2=0; //记得浮点数要用 doublecin>>cnt;while(cnt--){int num;cin>>num;if(num%5==0&&num%2==0)A+=num;if(num%5==1){B*=-1;B2+=num*B;B3=1;}if(num%5==2)C++;if(num%5==3){D+=num;D2++;}if(num%5==4){if(num>E)E=num;}}if(A==0)cout<<"N ";elsecout<<A<<' ';if(B3==0)cout<<"N ";elsecout<<B2<<' ';if(C==0)cout<<"N ";elsecout<<C<<' ';if(D2==0)cout<<"N ";elsecout<<setprecision(1)<<fixed<<D/D2<<' ';if(E==0)cout<<"N";elsecout<<E;return 0;
}

本代码是每输入一个数就判断一个数,会比较方便和省时

补充关于浮点数精度的知识点


1015 德才论


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct student
{char id[10];int de, cai, sum;int flag;
}stu[1000010]; //根据题目要求元素小于等于 10的 5次方
bool cmp(student a,student b) //关键代码,用于分类排序
{if (a.flag != b.flag)  return a.flag < b.flag; //按类分 else if (a.sum != b.sum) return a.sum>b.sum; //按总分 else if (a.de != b.de)  return a.de > b.de; //按德分 else return strcmp(a.id, b.id) < 0; //按准考证号分
}
int main()
{int n, l, h;scanf("%d%d%d", &n, &l, &h);int m = n;  //m表示及格人数for (int i = 0; i < n; i++){scanf("%s %d %d", &stu[i].id, &stu[i].de, &stu[i].cai); stu[i].sum = stu[i].de + stu[i].cai; //先把总分算出来有利于后期处理 if (stu[i].de < l || stu[i].cai < l) //先找出不及格者{stu[i].flag = 5;m--;}else if (stu[i].de >= h&&stu[i].cai >= h){stu[i].flag = 1;}else if (stu[i].de >= h&&stu[i]. cai < h){stu[i].flag = 2;}else if (stu[i].de >= stu[i].cai){stu[i].flag = 3;}else{stu[i].flag = 4;}}sort(stu, stu + n, cmp);printf("%d\n", m);for (int i = 0; i < m; i++){printf("%s %d %d\n", stu[i].id, stu[i].de, stu[i].cai); //这里用 printf,不然换做 cout会超时 }return 0;
}

本代码总体是:先分类,再排序

题目要求:考生按输入中说明的规则从高到低排序,当某类考生中有多人总分相同时,按其德分降序排列,若德分也并列,则按准考证号的升序输出

补充一点:strcmp 的用处如下

基本形式为 strcmp(str1,str2)
若str1=str2,则返回零
若str1<str2,则返回负数
若str1>str2,则返回正数
(即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止)

故代码中的 strcmp(a.id, b.id) < 0 代表升序(从小到大),记住这种表达方式~


1017 A除以B

#include <iostream>
using namespace std;
int main()
{string s;int a, t = 0, temp = 0; //t是商 temp是余数 cin >> s >> a;int len = s.length(); //记住长度用这种写法 t = (s[0] - '0') / a; //字符转换成数字,再求商 if ((t != 0 && len > 1) || len == 1) //之所以额外加 len,是因为后面的 for循环是从 i=1开始 cout << t;temp = (s[0] - '0') % a;for (int i = 1; i < len; i++) //从 i=1开始 {t = (temp * 10 + s[i] - '0') / a;cout << t;temp = (temp * 10 + s[i] - '0') % a;}cout << " " << temp;return 0;
}

代码内容是:模拟手动除法的过程,每次用第一位去除以B,如果得到的商不是0就输出,否则就 * 10 + 下一位,直到最后的数为余数
注意:存在除数为 0 的情况


1023 组个最小数

#include<iostream>
using namespace std;
int main()
{int book[10]={0};for(int i=0;i<10;i++)cin>>book[i];int index=1; //从 1开始,因为 0不能做开头while(book[index]==0) //没有 1再往后找开头index++;book[index]--;cout<<index;index=0;while(index<=9){if(!book[index]){index++;continue; }cout<<index;book[index]--;}return 0;
}

也是用到了小技巧:先确定开头,再往后从小到大添加数字


1024 科学计数法

#include<iostream>
#include<cstring>
using namespace std;
int main()
{char str[10010];cin>>str;int len = strlen(str);if(str[0]=='-') cout<<'-';int pos=0; //E的下标 while(str[pos]!='E') pos++;int exp=0; //指数 for(int i=pos+2;i<len;i++) //这里是 <号是因为 len代表长度而不是下标 exp=exp*10+(str[i]-'0');if(exp==0) //指数为 0的特例 for(int i=1;i<=pos;i++){cout<<str[i];return 0; //一定要记得 }if(str[pos+1]=='-') //指数为负数:考虑 0的位置 {cout<<"0.";for(int i=1;i<exp;i++) cout<<'0';cout<<str[1]; //省去了不必要的麻烦 for(int i=3;i<pos;i++) cout<<str[i];}else //指数为正数:考虑 .的位置 {for(int i=1;i<pos;i++){if(str[i]=='.') continue; //直接略过原来的 .号cout<<str[i];if(exp<pos-3&&i==exp+2) cout<<'.'; //注意条件 } for(int i=0;i<exp-(pos-3);i++) cout<<'0'; //注意条件 }return 0;
}

本题难点就在于条件

代码顺序是:先打出指数是0的特例(省去后面麻烦),再判断指数为正与负并写出各个结果

当指数为正数时,可以直接略过原来的 .号只考虑如何加新的 . 号,其中 (i==exp+2) && (pos-3 != exp) 可以打个草稿分析(前者+2是加上了符号和 . 号)(后者-3是减去了符号和 . 号和 E 号)

注意题目要求:正数不用输出+号、并保证所有有效位都被保留,包括末尾的 0


1025 反转链表


#include<iostream>
#include<algorithm>
using namespace std;
int main()
{int first, k, n, temp;cin >> first >> n >> k; //首地址,节点个数,反转数int *data = new int[100005];int *next = new int[100005]; //这两个用了new,不然运行出现栈溢出int list[100005]; //数组list用来按顺序存放地址//address data nextfor (int i = 0; i < n; i++) //结点地址,该结点保存的整数数据,下一结点的地址 {cin >> temp;cin >> data[temp] >> next[temp];}int sum = 0; //真正在链表中的结点个数//建立正序地址数组listwhile (first != -1) //巧妙的用法{list[sum++] = first;first = next[first];}//反转for (int i = 0; i < sum/k ; i++) //此处的 sum 代表元素个数而不是下标,不然要先 sum--{reverse(begin(list) + i*k, begin(list) + i*k + 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]]); // list没有存最后一个数,所以要打印 -1delete[] data;delete[] next;return 0;
}

主要步骤:先对原列表排序,再反转
注意:应该考虑输入样例中有不在链表中的结点的情况,所以用个sum计数
补充:new和delete的用法回顾 、reverse()函数的第二个参数是数组最后一个元素的下一个地址


1026 程序运行时间

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{int time1,time2;cin>>time1>>time2;double time=(time2-time1)/100.0-(time2-time1)/100; //整型除以浮点型得浮点型if(time>=0.5)                                      //整形减去浮点型也得浮点型time=(time2-time1)/100+1;elsetime=(time2-time1)/100;int sp=time;printf("%02d:%02d:%02d\n",sp/3600,sp%3600/60,sp%3600%60);return 0;
}

题目本身不难,但注意代码的技巧性,本题运用了四舍五入的简易版


1027 打印沙漏

#include<iostream>
#include<cmath>
using namespace std;
int main()
{int n;char c;cin >> n >> c;int judge = ( n + 1) / 2;int hang = sqrt(judge); //半个的完整的沙漏层数int res = n - 2 * hang * hang + 1; //多余沙漏数 for(int i = 1; i <= hang ; i ++){int num1 = i - 1;for(int j = 0 ; j < num1 ; j ++)cout << ' ';int num2 = 2 * (hang - i + 1 ) - 1;for(int j = 0; j < num2 ; j ++)cout << c;cout << endl;}for(int i = 2 ; i <= hang ; i++){int num1 = hang - i;for(int j = 0 ; j < num1 ; j ++)cout << ' ';int num2 = 2 * i - 1;for(int j = 0; j < num2 ; j++)cout << c;cout << endl;}cout << res ;return 0;
}

分析
明确了 整个沙漏的字符数(2k*k-1)与 半个的完整的沙漏层数(k)与 每一层沙漏个数(2k-1) 的关系即可

整个沙漏的形状为: 2k-1 , 2k-3 , … ,5 ,3, 1 , 3, 5, … ,2k-3 , 2k-1 ( k 代表半个的完整的沙漏层数)

整个沙漏的字符数为 2k*k -1 (用到了等差数列求和)

补充:对于输入的 n ,其实也只需要判断(n+1)/2是不是开方数,但是不管它是不是开方数,开方的结果都是 k

等差数列求和回顾


1028 人口普查

#include<iostream>
#include<cstring>
using namespace std;
int main()
{string date,date_old="2014/09/06",date_young="1814/09/06";string name,name_old,name_young;int n,count=0;cin>>n;while(n--){cin>>name>>date;if(date>="1814/09/06"&&date<="2014/09/06"){count++;if(date<date_old){date_old=date;name_old=name;}if(date>date_young){date_young=date;name_young=name;}}}if(count)cout<<count<<' '<<name_old<<' '<<name_young;elsecout<<0;return 0;
}

注意题目:这里确保每个输入的日期都是合法的,但不一定是合理的

分析:利用 string 的特性,使字符串间可以直接比较大小,然后求最值即最年轻的和最年长的,最后注意一个坑点,可能会有0个符合的年份,则只输出0


1029 旧键盘

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{char str1[90], str2[90];cin>>str1>>str2;int len1 = strlen(str1);int len2 = strlen(str2);int Bool[128] = {0}; //因为 ASCII码表里的字符总共有 128个 for (int i = 0; i < len1; i++){int j = 0; //重新计数 char c1;char c2;for ( j ; j < len2; j++){c1 = str1[i];c2 = str2[j];if (c1 >= 'a'&&c1 <= 'z') c1 -= 32; //保证只在此循环内改变字母大小写 if (c2 >= 'a'&&c2 <= 'z') c2 -= 32;if (c1 == c2) break; //在str2找得到str1的字符 }if (j == len2 && Bool[c1] == 0) //判断输出并记录{cout<<c1;Bool[c1] = 1;}}return 0;
}

题目和代码本身不难,但要记住这种判断输出并记录 的方法


1030 完美数列

#include <iostream>
#include <algorithm>
using namespace std;
int n, q, a[100010];
int binarySearch(int i, long long x) //二分法 找到第一个大于或者等于a[i]*q的数
{if (a[n - 1] <= x)  return n;int left = i + 1, right = n - 1, mid; //此处赋值与二分法无关while (left < right){mid = (left + right) / 2;if (a[mid] <= x) left = mid + 1;else right = mid;}return left; //right也可以
}
int main()
{cin>>n>>q;for(int i = 0; i < n; i++) cin>>a[i]; sort(a, a + n);int ans = 1;for (int i = 0; i < n; i++){int j = binarySearch(i,(long long)a[i] * q);ans = max(ans, j - i);}cout<<ans;return 0;
}

有点阅读理解的味道,题目的意思:最大与最小相除小于p,求如何使数列中间的元素更多
分析:从最小数开始,不断增大,找出 与 最大数开始往后减小(用二分法)的最下标大差值
补充:就当回顾二分法的适用题目与方法


1033 旧键盘打字

#include<iostream>
#include<ctype.h>
using namespace std;
int main()
{char a[128]={0},c;while((c=getchar())!='\n')a[c]=1;while((c=getchar())!='\n')if((isupper(c) && a[43]) || a[toupper(c)]==1) continue;else cout<<c;return 0;
}

题目和代码本身简单,但需要掌握两个函数:isupper() 和 toupper()

isupper(c): 当参数c为大写英文字母(A-Z)时,返回 非0 值,否则返回 0

toupper(c): 能将字符c转换为大写英文字母

两者都包含于头文件 <ctype.h>


1034 有理数四则运算

#include <iostream>
#include <cmath>
using namespace std;
long long a, b, c, d;
long long gcd(long long a, long long b)
{return b == 0 ? a : gcd(b, a % b);
}
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;
}

分析func(m, n)的作用是对 m / n 的分数进行化简
在 func()函数中,先看 m 和 n 里面是否有0(m * n 是否等于0),再看 flag,flag=true表示 m 和 n 异号,那么后面要添加”(-“”)”,再将 m和 n 取绝对值,然后计算 x = m/n,x 表示可提取的整数部分(带分数),接着根据 m % n 是否等于 0 的结果判断后面还有没有小分数,若有小分数,然后求 m 和 n 的最大公约数 t ,让 m 和 n 都除以 t 进行化简,最后输出即可

补充:判断 m 和 n 是否异号千万不要写成判断 m * n 是否小于 0,因为 m * n 的结果可能超过了 long long int 的长度,导致溢出大于 0,如果这样写的话会有一个测试点无法通过


1035 插入与归并


#include <iostream>
#include <algorithm>
using namespace std;
int main()
{int n, a[100], b[100], i, j;cin >> n;for (int i = 0; i < n; i++)cin >> a[i];for (int i = 0; i < n; i++)cin >> 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 *= 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(" "); //类似于 firstblood printf("%d", a[j]);}return 0;
}

先简单回顾一下插入排序(图1)和归并排序(图2)


分析:先将 i 指向序列中满足从左到右是从小到大顺序的最后一个下标,再将 j 指向从 i + 1 开始,第一个不满足a[j] == b[j]的下标,如果 j 顺利到达了下标 n,说明是插入排序,否则说明是归并排序

过程:如果是插入排序,再下一次的序列是sort(a, a+i+2)(这里别忘了 sort 函数第二个参数 + 的是数量),而如果是归并排序,直接对原来的序列进行模拟归并过程

补充:插入排序是由前之后排序,归并排序是整体排序,一次排一列


1039 到底买不买


#include<iostream>
using namespace std;
int main()
{int a[128]={0};char tmp;while((tmp=getchar())!='\n') //买 {a[tmp]++;}while((tmp=getchar())!='\n') //想要 {a[tmp]--;}int firstblood=1,votal1=0,votal2=0;for(int i=0;i<128;i++){if(a[i]<0){if(firstblood)firstblood=0;votal1+=-a[i]; //可以直接写负数}else{votal2+=a[i];}}if(firstblood){cout<<"Yes "<<votal2;}elsecout<<"No "<<votal1;return 0;
}

比较奇特的一点是:由 一个数组 和 数组的各个值的正负 判断 Yes or No
分析:想要记作 - - ,买了的记作++ ,那么最后 正数即为多余的,负数即为缺少的


1042 字符统计

#include<iostream>
#include<ctype.h>
using namespace std;
int main()
{int alpha[26]={0};char ch;while((ch=getchar())!='\n')if(isalpha(ch)) //判断是否为英文字母alpha[tolower(ch)-'a']++; //根据题意 ,只存小写英文字母int _max=0,sp;for(int i=25;i>=0;i--)if(alpha[i]>=_max)sp=i,_max=alpha[i];cout<<char(sp+'a')<<' '<<_max; //用 char(int)的方式输出字符return 0;
}

题目和代码本身简单,但需要掌握两个函数:isalpha() 和 tolower()

isalpha(c): 当参数c为英文字母 (A-Z) 或 (a-z) 时,返回 非0 值,否则返回 0

isalnum(c):当字符变量c为字母或数字,返回 非0 值,否则返回 0

tolower(c): 能将字符c转换为小写英文字母

两者都包含于头文件 <ctype.h>


1044 火星数字

#include <iostream>
#include <cctype>
#include <string>
using namespace std;
string a[13] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"}; // 0到 12的火星文
string b[13] = {"####", "tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"}; //进位以后的 12个高位数字
string s;
int len;
void tomars(int t) //转字符串
{if (t / 13) cout << b[t / 13]; //进位数 if ((t / 13) && (t % 13)) cout << " "; //有个位数 if (t % 13 || t == 0) cout << a[t % 13]; // 0 或 个位数
}
void toearth() //转数字
{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 < 13; j++){if (s1 == a[j] || s2 == a[j]) t2 = j;if (s1 == b[j]) t1 = j;}cout << t1 * 13 + t2;
}
int main()
{int n;cin >> n;getchar(); //非常关键!for (int i = 0; i < n; i++){getline(cin, s);len = s.length();if (isdigit(s[0])){int num=0;for(int i=0;i<s.length();i++)num=num*10+(s[i]-'0'); //注意不是 +=tomars(num);}elsetoearth();cout << endl;}return 0;
}

分析:本代码用到了 string 数组,和各种各样的函数

substr ( 开始,长度),相当于复制,举例如下:

string s1 = s2.substr(0, 3);

atoi(c) 将字符 c 转换成数字,举例如下:

str[100]="00100" ; cout<<atoi(str)<<endl; output:100
//头文件 <cstdlib>

strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数
strcpy(str1,str2),将str2中的字符复制到str1

补充

string s;
getline(cin,s);
cout<<s.size();//下面实例说明了空格在 getline中也算一个字符
input:a b
output:3

1045 快速排序

#include <iostream>
#include <algorithm>
using namespace std;
int v[100000];
using namespace std;
int main()
{int n, _max = 0, cnt = 0;cin>>n;int a[100005],b[100005];for (int i = 0; i < n; i++){scanf("%d", &a[i]);b[i] = a[i];}sort(a,a+n);for (int i = 0; i < n; i++){if(a[i] == b[i] && b[i] > _max)v[cnt++] = b[i];if (b[i] > _max)_max = b[i];}cout<<cnt<<endl;for(int i = 0; i < cnt; i++){if (i != 0) cout<<' ';cout<<v[i];}cout<<endl;return 0;
}

主元的定义:比前面的数都大,比后面的数都小,而相当于主元本身已经排好序了,那么将原数组排好序,对应位置相同的有可能是主元,再加上当前最大值的验证方法,一重循环即可

然后我似乎也找到有些人热衷于在末尾加换行符的原因了:不加就可能过不了测试点


1048 数字加密

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{string a, b, c;cin >> a >> b;int lena = a.length(), lenb = b.length();reverse(a.begin(), a.end()); //反转字符串reverse(b.begin(), b.end());if (lena > lenb) b.append(lena - lenb, '0');elsea.append(lenb - lena, '0');char str[14] = {"0123456789JQK"};for (int i = 0; i < a.length(); i++){if (i % 2 == 0)c += str[(a[i] - '0' + b[i] - '0') % 13];else{int temp = b[i] - a[i];if (temp < 0) temp = temp + 10;c += str[temp];}}for (int i = c.length() - 1; i >= 0; i--)cout << c[i];return 0;
}

题目和代码本身简单,但需要注意:题目并没有说明白数字A可能比数字B长,这时需要短的数字补零(反转字符串即可)

补充:关于 string类append 的用法,举例如下:

string a, b;
cin >> a >> b;
int lena = a.length(), lenb = b.length();
if (lena > lenb) //若 a 比 b 长,就补齐 bb.append(lena - lenb, '0'); //第一个参数是个数,第二个参数是补充的字符
cout << b;input:12345 123
output:12300

1049 数列的片段和

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{int n;cin >> n;double sum = 0.0, temp;for (int i = 1; i <= n; i++){ cin >> temp;sum += temp * i * (n - i + 1);}printf("%.2f", sum);return 0;
}

单纯就是数学题,每个数出现的个数是(左边数的个数+1)*(右边数的个数+1)


1050 螺旋矩阵

#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; //t代表总个数-1 scanf("%d", &N);for (n = sqrt((double)N); n >= 1; n--) //计算矩阵(sqrt只适用于浮点型) {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; // level层数算法 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;
}

解析:首先计算行数m列数n的值,题目要求(m >= n),n 等于根号N的整数部分一直往 1 减,直到 N % n == 0,m 等于 N / n,再算 层数level(尽量多),等于 m / 2 + m % 2(所以用 m),最后矩阵排序,按顺时针螺旋方向,数字从大到小,输入数组元素,最后正常输出数组即可

补充:输入数组元素时,按照 4 个 for 循环输入,注意第二个和第四个 for 循环的起始位置和结束位置(输入元素总量少一个)

不想占用栈太多内存时,可以用 vector 充当动态数组,因为其数据存储在堆中,用法如下:

//建立一维动态数组
vector<int> a(10);
//建立二维动态数组
vector<vector<int> > b(10, vector<int>(10));
//输入
cin>>a[0]>>b[0][0];
//输出
cout<<a[0]<<' '<<b[0][0];input:5 5
output:5 5

1051 复数乘法

#include <cstdio>
#include <cmath>
int main() {double a,b,c,d;scanf("%lf %lf %lf %lf",&a,&b,&c,&d);double a1,a2;a1=a*c*cos(b+d);//三角复合函数a2=a*c*sin(b+d);//三角复合函数if (fabs(a1)<=0.01)a1=0.00;if (fabs(a2)<=0.01)a2=0.00;if (a2<0)printf("%.2lf-%.2lfi",a1,-a2);elseprintf("%.2lf+%.2lfi",a1,a2);return 0;
}

题目可能有问题,没有说清楚要不要四舍五入,顺便补充一个关于四舍五入小知识点

double a=0.004;
double b=0.005;
printf("%.2lf\n",a);
printf("%.2lf",b);//从结果来看,对于格式化,程序会自动四舍五入
output:0.000.01

1052 卖个萌


#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<vector<string> > v; //二重动态数组 for(int i = 0; i < 3; i++){string s;getline(cin, s);vector<string> row;int j = 0, k = 0;while(j < s.length()){if(s[j] == '['){while(k++ < s.length()){if(s[k] == ']'){row.push_back(s.substr(j+1, k-j-1)); //(长度,开始)break;}}}j++;}v.push_back(row);}int n;cin >> n;for(int i = 0; i < n; i++){int a, b, c, d, e;cin >> a >> b >> c >> d >> e;if(a > v[0].size() || b > v[1].size() || c > v[2].size() || d > v[1].size() || e > v[0].size() || a < 1 || b < 1 || c < 1 || d < 1 || e < 1){cout << "Are you kidding me? @\\/@" << endl;continue;}cout << v[0][a-1] << "(" << v[1][b-1] << v[2][c-1] << v[1][d-1] << ")" << v[0][e-1] << endl; //注意减一 }return 0;
}

问题和代码本身不难,但用到了二重动态规划(string类型),举例如下:

vector<vector<string> > v; //注意对于 string 类型不用设置长度
vector<string> row;
string a;
cin>>a;
row.push_back(a);
v.push_back(row);
cout<<v[0][0];input:12345
output:12345

1054 求平均值


#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
int main()
{int n, cnt = 0;char a[50], b[50];double temp, sum = 0.0;cin >> n;for(int i = 0; i < n; i++){scanf("%s", a);sscanf(a, "%lf", &temp); //将 a 输入到 temp 中sprintf(b, "%.2f",temp); //将 temp 输入到 b 中int flag = 0;for(int j = 0; j < strlen(a); j++) //以 a 为参照物进行筛选排除,很关键的一步操作if(a[j] != b[j]) flag = 1;if(flag || temp < -1000 || temp > 1000){printf("ERROR: %s is not a legal number\n", a);continue;}else{sum += temp;cnt++;}}if(cnt == 1)printf("The average of 1 number is %.2f", sum);else if(cnt > 1)printf("The average of %d numbers is %.2f", cnt, sum / cnt);elseprintf("The average of 0 numbers is Undefined");return 0;
}

先讲解 sscanfsprintf 的作用:
sscanf(a,"%lf",&temp):从 a(字符串)中读进与指定格式相符的数据输入到 temp 中
sprintf(b,"%.2f",temp):将 temp 格式化输入到 b(字符串)中 (注意没有&号)

下面以部分本代码举例:

char a[50], b[50];
double temp;
scanf("%s", a);
sscanf(a, "%lf", &temp);
sprintf(b, "%.2f",temp);
cout<<a<<endl<<temp<<'  '<<b;//从下列实例看出排除了多种不合法数据
input:1.23.4
ouput:1.23  1.23
input:45.678
ouput:45.678  45.68
input:aaa
ouput:4.94066e-324  0.00

以其他例子举例:

char str[100]="1234:3.14,hello",arr[10];
int n;
double db;
sscanf(str,"%d:%lf,%s",&n,&db,arr); //注意这里不能用 %.2lf(似乎是规定)
cout<<n<<' '<<db<<' '<<arr<<endl;char str2[100],arr2[]="hello";
int n2=1234;
double db2=3.14;
sprintf(str2,"%d:%.2lf,%s",n2,db2,arr2);
cout<<str2;//下面实例说明此函数可以转换多个参数
output:
1234 3.14 hello
1234:3.14,hello

1055 集体照


#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct node
{string name;int height;
};
int cmp(struct node a, struct node b)
{return a.height != b.height ? a.height > b.height : a.name < b.name; //掌握这种表达方法
}
int main()
{int n, k, m; //人数 排数 某一行 cin >> n >> k;vector<node> stu(n); //直接用 vector 容器代替了数组 for(int i = 0; i < n; i++){cin >> stu[i].name >> stu[i].height;}sort(stu.begin(), stu.end(), cmp);int t = 0, row = k; // t代表排序进行的位置 row代表当前行数 while(row--){if(row == k-1) //最后一排m = n - n / k * (k - 1);elsem = n / k;vector<string> ans(m);ans[m / 2] = stu[t].name;// 左边一列int j = m / 2 - 1;for(int i = t + 1; i < t + m; i = i + 2)ans[j--] = stu[i].name;// 右边一列j = m / 2 + 1;for(int i = t + 2; i < t + m; i = i + 2)ans[j++] = stu[i].name;// 输出当前排cout << ans[0];for(int i = 1; i < m; i++)cout << " " << ans[i];cout << endl;t = t + m;}return 0;
}

分析:因为是面对拍照者,后排的人输出在上方,前排输出在下方,每排人数为 N / K,多出来的人全部站在最后一排,最中间一个学生应该排在 m / 2 的下标位置(题目的要求有歧义),即ans[m / 2] = stu[t].name,然后排左边一列,ans 数组的下标 j 从 m / 2 - 1 开始,一直往左 j - -,而对于 stu 的下标 i 从 t + 1 开始,每次隔一个人选取,排右边的队伍同理,最后输出当前已经排好的 ans 数组


1056 组合数的和

#include <cstdio>
#include<iostream>
using namespace std;
int main()
{int N, sum = 0, temp;scanf("%d", &N);for (int i = 0; i < N; i++){scanf("%d", &temp);sum += temp * 10 * (N - 1) + temp * (N - 1);}printf("%d", sum);return 0;
}

技巧性题目,在 sum 累加的过程中,对于每一个输入的数字 temp,能够放在个位也能够放在十位,所以每个数字 temp 都能在个位出现 (N-1) 次,十位出现 (N-1) 次,在个位产生的累加效果为 temp * (N-1) ,而在十位产生的累加效果为 temp * (N-1) * 10,所以 sum 即是答案


1058 选择题

#include <cstdio>
#include <vector>
#include <set>
using namespace std;
int main()
{int n, m, temp, k; //学生人数 多选题的个数 选项个数 正确选项个数 scanf("%d%d", &n, &m);vector<set<char> > right(m); //题目选项 vector<int> total(m), wrongCnt(m); //每一题的总分 某题错误的人数 for(int i = 0; i < m; i++){scanf("%d%d%d", &total[i], &temp, &k);for(int j = 0; j < k; j++){char c;scanf(" %c", &c);right[i].insert(c);}}for(int i = 0; i < n; i++){int score = 0;scanf("\n"); //表示忽略回车键 for(int j = 0; j < m; j++){if(j != 0) scanf(" ");scanf("(%d", &k);set<char> st;char c;for(int l = 0; l < k; l++){scanf(" %c", &c);st.insert(c);}scanf(")");if(st == right[j]) //st集合和该题目选项一致 score += total[j];elsewrongCnt[j]++;}printf("%d\n", score);}int maxWrongCnt = 0;for(int i = 0; i < m; i++)maxWrongCnt=max(maxWrongCnt,wrongCnt[i]);if(maxWrongCnt == 0)printf("Too simple");else{printf("%d", maxWrongCnt);for(int i = 0; i < m; i++)if(wrongCnt[i] == maxWrongCnt) //输入编号 printf(" %d", i + 1);}return 0;
}

分析:本题只要思路明确就可以很容易写出代码,要求输出 每个人的分数 和 错误人数最多的题和编号,那么肯定要设置 wrongCnt[i] 作为每题错误人数,maxWrongCnt作为错误最多的人数,再根据题目要求设置相应的变量进行推导即可
解析:set 是一种特别好的容器,它能存储元素各不同元素作为集合,举例如下:

set<int> st,sp;//判断两种集合包含元素是否相同 (直接用等号判断)
st==sp;//插入 (括号内是元素值)
st.insert(5)//查找 (括号内是元素值;若找不到则返回 st.end())
st.find(5)!=st.end()//是否存在 (括号内是元素值;若找到返回非 0值;若找不到则返回 0)
st.count(5)

小知识:vector 封装数组,set 封装集合,queue 封装队列,list 封装链表,map 封装关联数
补充:关于 scanf()另类的用法,举例如下:

int d;
scanf("("); //表示忽略左括号
scanf("%d",&d);
scanf(")"); //表示忽略右括号
cout<<d;//下面实例论证上述说明是正确的
input: (45)
output: 45input: 45
output: 45input: )45(
output: 0

1059 C语言竞赛


#include <iostream>
#include <set>
#include <cmath>
using namespace std;
int ran[10000];
bool isprime(int a)
{if(a <= 1) return false;for(int i = 2; i <= sqrt((double)a); i++)if(a % i == 0)return false;return true;
}
int main()
{int n, k;scanf("%d", &n);for(int i = 0; i < n; i++){int id;scanf("%d", &id);ran[id] = i + 1;}scanf("%d", &k);set<int> ss;for(int i = 0; i < k; i++){int id;scanf("%d", &id);printf("%04d: ", id);if(ran[id] == 0){printf("Are you kidding?\n");continue;}if(ss.find(id) == ss.end())ss.insert(id);else{printf("Checked\n");continue;}if(ran[id] == 1)printf("Mystery Award\n");else if(isprime(ran[id]))printf("Minion\n");elseprintf("Chocolate\n");}return 0;
}

题目和代码本身不难,就是想提醒一下以后遇到素数不要老想着用欧拉线性筛法,记一下普通模板

bool isprime(int a)
{if(a <= 1) return false;for(int i = 2; i <= sqrt((double)a); i++)if(a % i == 0)return false;return true;
}

1060 爱丁顿数

#include <iostream>
#include <algorithm>
using namespace std;
int a[100005];
bool cmp(int a, int b)
{return a > b;
}
int main()
{int n;scanf("%d", &n);for(int i = 1; i <= n; i++) scanf("%d", &a[i]); //注意要从 1 开始,因为天数不能为 0sort(a+1, a+n+1, cmp);int ans = 0, p = 1;while(ans < n && a[p] > p){ans++;p++;}printf("%d", ans);return 0;
}

单纯就是考数学,从下标 1 开始存储 n 天的公里数在数组 a 中,对 n 个数据从大到小排序,i 表示了骑车的天数,那么满足a[i] > i的最大值即答案


1064 朋友数

#include <iostream>
#include <set>
using namespace std;
int getFriendNum(int num)
{int sum = 0;while(num != 0){sum += num % 10;num /= 10;}return sum;
}
int main()
{set<int> s;int n, num;scanf("%d", &n);for(int i = 0; i < n; i++){scanf("%d", &num);s.insert(getFriendNum(num));}printf("%d\n", s.size());set<int>::iterator it; //迭代器for(it = s.begin(); it != s.end(); it++){if(it != s.begin()) printf(" "); //与 firstblood神似printf("%d", *it);}return 0;
}

题目和代码本身不难,但补充几个有关 set 的小知识点:

//1 定义时不用写集合个数,因为不常用到下标
set<int> s;//2 关于 int型的元素会自动排序,不用手动 sort//3 输出 set元素不用下标法,用如下方法
for(auto it = s.begin(); it != s.end(); it++) //auto 非常好用,但只适用于 PATset<int>::iterator it; //迭代器
for(it = s.begin(); it != s.end(); it++) //蓝桥杯和 PAT都适用
//也是关于蓝桥杯和 PAT的
stoi()//此函数仅适用于 PAT
string str = "010";
int a = stoi(str, 0, 2); //将 2进制转换成 10进制
int b = stoi(str, 0, 3); //将 3进制转换成 10进制
cout << a << endl;
cout << b << endl;//中间的参数是起始位置,但最好别改(改了会报错,我也不知道怎么回事)
output:
2
3

1067 试密码


#include <iostream>
using namespace std;
int main()
{string password, temp;int n, cnt = 0;cin >> password >> n;getchar(); //当后面有 getline时必要的操作 while(1){getline(cin, temp);if (temp == "#") break;cnt++;if (cnt <= n && temp == password){cout << "Welcome in";break;}else if (cnt <= n && temp != password){cout << "Wrong password: " << temp << endl;if (cnt == n){cout << "Account locked";break;}}}return 0;
}

题目和代码本身简单,但想回顾一下字符串的边界与 getchar()的必要性


1068 万绿丛中一点红


#include <iostream>
#include <vector>
#include <map>
using namespace std;
int m, n, tol;
vector<vector<int> > v;
int dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
bool judge(int i, int j)
{for (int k = 0; k < 8; k++) //八种情况{int tx = i + dir[k][0];int ty = j + dir[k][1];if (tx >= 0 && tx < n && ty >= 0 && ty < m && v[i][j] - v[tx][ty] >= 0 - tol && v[i][j] - v[tx][ty] <= tol) return false;}return true;
}
int main()
{int cnt = 0, x = 0, y = 0;scanf("%d%d%d", &m, &n, &tol); //横坐标 纵坐标 差值v.resize(n, vector<int>(m));map<int, int> mapp;for (int i = 0; i < n; i++){for (int j = 0; j < m; j++){scanf("%d", &v[i][j]);mapp[v[i][j]]++;}}for (int i = 0; i < n; i++){for (int j = 0; j < m; j++){if (mapp[v[i][j]] == 1 && judge(i, j) == true){cnt++;x = i + 1;y = j + 1;}}}if (cnt == 1)printf("(%d, %d): %d", y, x, v[x-1][y-1]);else if (cnt == 0)printf("Not Exist");elseprintf("Not Unique");return 0;
}

分析:思路大致和迷宫问题类似,但此题有个额外注意的地方,即判定条件,仅当满足在 m坐标 与 n坐标内 和 差值在200之间才判定为否,其他均为正确,这样做的好处就是判断越界的元素也是对的(相当于在边界的元素只用判定相邻的元素即可)

补充vector 二维数组的两种表达方式,举例如下:

int n=5,m=5;//第一种
vector<vector<int> > v(n, vector<int>(m));//第二种 (安全些,不容易发生段错误)
vector<vector<int> > v;
v.resize(n, vector<int>(m));

额外:vector 的 resize 另一种用法

补充:有关 map 的用法,举例如下:

map<int,int> m;
m[1000]++;
m[100]++;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++)cout << (*it).first << " " << (*it).second << endl;//说明 map可直接用来存储关联数,并可以有效提取
output:
100 1
1000 1m[1]=5,m[2]=4,m[3]=3,m[4]=2,m[5]=1;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++){cout << (*it).first << " " << (*it).second << endl;//再一次论证
output:
1 5
2 4
3 3
4 2
5 1

1069 微博转发抽奖


#include <iostream>
#include <map>
using namespace std;
int main()
{int m, n, s;scanf("%d%d%d", &m, &n, &s); //转发的总量 中奖间隔 第一位中奖者序号string str;map<string, int> mapp; //新用法bool flag = false;for (int i = 1; i <= m; i++){cin >> str;if (mapp[str] == 1) s = s + 1; //写在循环前面if (i == s && mapp[str] == 0){mapp[str] = 1;cout << str << endl;flag = true;s = s + n;}}if (flag == false) cout << "Keep going...";return 0;
}

这个代码很机智的将复杂简化了,若是一般写容易i+n,但本代码将全部循环写出来,并用另一个变量 s 代替 + n,这样就避免代码复杂化


1070 结绳

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{int n;scanf("%d", &n);vector<int> v(n);for (int i = 0; i < n; i++)scanf("%d", &v[i]);sort(v.begin(), v.end());int result = v[0];for (int i = 1; i < n; i++)result = (result + v[i]) / 2;printf("%d", result);return 0;
}

数学题,因为所有长度都要串在一起,所以越早串绳子,越要对折的次数多,所以既然希望绳子长度是最长的,就必须让长的段对折次数尽可能的短


1073 多选题常见计分法


#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int main()
{int n, m, optnum, truenum, temp, maxcnt = 0;int hash[] = {1, 2, 4, 8, 16}, opt[1005][105] = {0}; //每一个选项 学生选项 char c;scanf("%d %d", &n, &m); //学生人数 多选题的个数vector<int> fullscore(m), trueopt(m); //满分值 正确选项  vector<vector<int> > cnt(m, vector<int>(5)); //错误次数for (int i = 0; i < m; i++){scanf("%d %d %d", &fullscore[i], &optnum, &truenum); //满分值 选项个数(没用) 正确选项个数for (int j = 0; j < truenum; j++) {scanf(" %c", &c); //所有正确选项trueopt[i] += hash[c-'a'];}}for (int i = 0; i < n; i++){double grade = 0; for (int j = 0; j < m; j++){scanf("\n");scanf("(%d", &temp);for (int k = 0; k < temp; k++){scanf(" %c", &c);opt[i][j] += hash[c-'a'];}scanf(")", &c);int el = opt[i][j] ^ trueopt[j]; //异或if (el){if ((opt[i][j] | trueopt[j]) == trueopt[j])grade += fullscore[j] * 1.0 / 2; //漏选 得一半分 if (el)for (int k = 0; k < 5; k++)if (el & hash[k]) cnt[j][k]++; //记录错误次数 }elsegrade += fullscore[j];}printf("%.1f\n", grade); //学生得分 }for (int i = 0; i < m; i++)for (int j = 0; j < 5; j++)maxcnt = max(maxcnt,cnt[i][j]); //最大错误次数if (maxcnt == 0)printf("Too simple\n");elsefor (int i = 0; i < m; i++)for (int j = 0; j < 5; j++)if (maxcnt == cnt[i][j])printf("%d %d-%c\n", maxcnt, i+1, 'a'+j); //错误次数 题目编号 选项(字母)return 0;
}

分析:用异或运算来判断一个选项和正确选项是否匹配,如果是匹配的,那么异或的结果应当是0,如果不匹配,那么这个选项就是存在错选或者漏选的情况,例如:设 a 为 00001,b 为 00010,c 为 00100,d 为 01000,e 为 10000,如果给定的正确答案是 ac,即 10001,那么如果给出的选项也是 10001,异或的结果就是 0,如果给出的选项是 a(漏选)或者 ab(选错),即 00001 或 00011,异或之后皆不为 0,通过 异或操作的结果与运算符 就可以把错选和漏选的选项找出来,本代码 存储选项的变量 存储的是二进制值,二进制由 hash 给出分别是1 2 4 8 16,即二进制各位置的 1(本题需要设置的变量特别多)

补充

运算符 描述
& 按位与运算符: 参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
| 按位或运算符: 只要对应的二个二进位有一个为1时,结果位就为1。
^ 按位异或运算符: 当两对应的二进位相异时,结果为1
~ 按位取反运算符: 对数据的每个二进制位取反,即把1变为0,把0变为1 ;~x 类似于 -x-1
<< 左移动运算符: 运算数的各二进位全部左移若干位,由 << 右边的数字指定了移动的位数,高位丢弃,低位补0。
>> 右移动运算符: 把">>"左边的运算数的各二进位全部右移若干位,>> 右边的数字指定了移动的位数

下面以变量 a 为 60,b 为 13 举例,二进制格式如下:

a = 0011 1100b = 0000 1101-----------------a & b = 0000 1100a | b = 0011 1101a ^ b = 0011 0001~a  = 1100 0011a << 2 = 1111 0000a >> 2 = 0000 1111

1074 宇宙无敌加法器

#include <iostream>
using namespace std;
int main()
{string s, s1, s2, ans;int carry = 0, flag = 0;cin >> s >> s1 >> s2;ans = s;string ss1(s.length() - s1.length(), '0'); //长度补齐 s1 = ss1 + s1;string ss2(s.length() - s2.length(), '0'); //长度补齐 s2 = ss2 + s2;for(int i = s.length() - 1; i >= 0; i--){int mod = s[i] == '0' ? 10 : (s[i] - '0');ans[i] = (s1[i] - '0' + s2[i] - '0' + carry) % mod + '0'; //当前位carry = (s1[i] - '0' + s2[i] - '0' + carry) / mod; //进制位}if (carry != 0) ans = '1' + ans;for(int i = 0; i < ans.size(); i++){if (ans[i] != '0' || flag == 1 || carry != 0){flag = 1;cout << ans[i];}}if (flag == 0) cout << 0;return 0;
}

代码非常简洁,但没什么好讲的,回顾一下 string 类进制


1075 链表元素分类


#include <iostream>
#include <vector>
using namespace std;
struct node
{int data, next;
}list[100000];
vector<int> v[3]; //很棒的选择 分三类
int main()
{int start, n, k, a;scanf("%d%d%d", &start, &n, &k);for (int i = 0; i < n; i++){scanf("%d", &a);scanf("%d%d", &list[a].data, &list[a].next);}while(start != -1){int data = list[start].data;if (data < 0) //在选择的类别里面尾插 v[0].push_back(start);else if (data >= 0 && data <= k)v[1].push_back(start);elsev[2].push_back(start);start = list[start].next;}int flag = 0;for (int i = 0; i < 3; i++){for (int j = 0; j < v[i].size(); j++){if (flag == 0){printf("%05d %d ", v[i][j], list[v[i][j]].data);flag = 1;}elseprintf("%05d\n%05d %d ", v[i][j], v[i][j], list[v[i][j]].data);}}printf("-1");return 0;
}

此题与1025类似,但那题用的是堆数组,而此题用的是 vector 数组,作用就是可以根据题目要求分三类,而要插入直接 push_back,非常的简洁(那题只用分一类,不用多此一举)


1076 Wifi密码


#include <iostream>
using namespace std;
int main()
{string s;while (cin >> s) if(s.size() == 3 && s[2] == 'T') cout << s[0]-'A'+1;return 0;
}

分析:超精简代码,n 没什么作用,以字符串方式接收输入,只要遇到任何一个字符串 s 满足大小为 3 且 s[2] 为 ‘T’,就将 s[0] 字母对应的 wifi 密码输出


1080 MOOC期终成绩


#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
struct node
{string name;int gp, gm, gf, g; //编程 期中 期末 总评
};
bool cmp(node a, node b)
{return a.g != b.g ? a.g > b.g : a.name < b.name;
}
map<string, int> idx; //用于记录排除编程后的结构体顺序
int main()
{int p, m, n, score, cnt = 1;cin >> p >> m >> n;vector<node> v, ans; //初始动态结构体数组 最终动态结构体数组 string s;for (int i = 0; i < p; i++){cin >> s >> score;if (score >= 200) //先实施排除编程 {v.push_back(node{s, score, -1, -1, 0}); //尾插结构体 idx[s] = cnt++;}}for (int i = 0; i < m; i++){cin >> s >> score;if (idx[s] != 0) v[idx[s] - 1].gm = score; //记录期中 }for (int i = 0; i < n; i++){cin >> s >> score;if (idx[s] != 0){int temp = idx[s] - 1; //顺序成员 v[temp].gf = v[temp].g = score; //先使总评为期末 if (v[temp].gm > v[temp].gf) v[temp].g = int(v[temp].gm * 0.4 + v[temp].gf * 0.6 + 0.5); //总评变化 }}for (int i = 0; i < v.size(); i++)if (v[i].g >= 60) ans.push_back(v[i]); //最后实施排除总评 sort(ans.begin(), ans.end(), cmp);for (int i = 0; i < ans.size(); i++)printf("%s %d %d %d %d\n", ans[i].name.c_str(), ans[i].gp, ans[i].gm, ans[i].gf, ans[i].g);return 0;
}

分析
1、因为所有人必须要G编程>=200分,所以用v数组保存所有G编程>=200的人,(一开始gm和gf都为-1),用map映射保存名字所对应v中的下标(为了避免与“不存在”混淆,保存下标+1,当为0时表示该学生的姓名在v中不存在)
2、G期中中出现的名字,如果对应的map并不存在(==0),说明该学生编程成绩不满足条件,则无须保存该学生信息。将存在的人的期中考试成绩更新
3、G期末中出现的名字,也必须保证在map中存在,先更新G期末和G总为新的成绩,当G期末<G期中时再将G总更新为(G期中x 40% + G期末x 60%)
4、将v数组中所有G总满足条件的放入ans数组中,对ans排序(总分递减,总分相同则姓名递增),最后输出ans中的学生信息


1084 外观数列

#include <iostream>
using namespace std;
int main()
{string s;int n, j;cin >> s >> n;for (int cnt = 1; cnt < n; cnt++){string t;for (int i = 0; i < s.length(); i = j){for (j = i; j < s.length() && s[j] == s[i]; j++); //注意是让 for循环单独循环下去 t += s[i] + to_string(j - i);}s = t;}cout << s;return 0;
}

分析:题目的意思是 d 代表某个数字来进行数列演示,而关于代码的关键部分,可以自行想象数字(字符串)迭代的过程

补充:

to_string()函数作用:将数字常量转换成 string 类
注意:此函数在 PTA 可以使用,而蓝桥杯不允许若要用可以使用代替的函数表示,举例如下:string  to_string(int a)
{string b="";char c[1000]={0}; //存储空间根据题目调整 sprintf(c,"%d",a);for(int i=0;i<strlen(c);i++)b+=c[i];return b;
}
//注意还有头文件 <cstring> 和 <cstdio>

1085 PAT单位排行


#include <iostream>
#include <algorithm>
#include <cctype>
#include <vector>
#include <unordered_map>
using namespace std;
struct node
{string school;int tws, ns; //分数 人数
};
bool cmp(node a, node b)
{if (a.tws != b.tws)return a.tws > b.tws;else if (a.ns != b.ns)return a.ns < b.ns;elsereturn a.school < b.school; //string 可以按 ASCII码排序
}
int main()
{int n;scanf("%d", &n);unordered_map<string, int> cnt; //人数 unordered_map<string, double> sum; //加权分数 for (int i = 0; i < n; i++){string id, school;cin >> id;double score;scanf("%lf", &score);cin >> school;for (int j = 0; j < school.length(); j++)school[j] = tolower(school[j]);if (id[0] == 'B')score = score / 1.5;else if (id[0] == 'T')score = score * 1.5;sum[school] += score;cnt[school]++;}vector<node> ans;for (auto it = cnt.begin(); it != cnt.end(); it++)ans.push_back(node{it->first, (int)sum[it->first], cnt[it->first]});  //新数组接收sort(ans.begin(), ans.end(), cmp);int rank = 0, pres = -1;printf("%d\n", (int)ans.size());for (int i = 0; i < ans.size(); i++){if (pres != ans[i].tws) rank = i + 1; //处理并列排名的妙招 pres = ans[i].tws;printf("%d ", rank);cout << ans[i].school;printf(" %d %d\n", ans[i].tws, ans[i].ns);}return 0;
}

分析:两个 map,一个 cnt 用来存储某学校名称对应的参赛人数,另一个 sum 计算某学校名称对应的总加权成绩,每次学校名称 string school 都要转化为全小写,将 map 中所有学校都保存在 vector ans 中,类型为 node,node 中包括学校姓名、加权总分、参赛人数,对 ans 数组排序,根据题目要求写好 cmp 函数,最后按要求输出,对于排名的处理:设立 pres 表示前一个学校的加权总分,如果 pres 和当前学校的加权总分不同,说明 rank 等于数组下标 + 1,否则 rank不变

补充

本代码用的头文件 <unordered_map> 仅适用于 PTA
而蓝桥杯只用用 <map>同理,蓝桥杯不能用 auto ,要转为迭代器类型

1086 就不告诉你


#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{int a, b;scanf("%d %d", &a, &b);string s = to_string(a * b);reverse(s.begin(), s.end());printf("%d", stoi(s));return 0;
}

单纯回顾一下 string 类 既有 to_string 又有 stoi 还可以 reverse


1089 狼人杀-简单版


#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main()
{int n;cin >> n;vector<int> v(n+1);for (int i = 1; i <= n; i++) cin >> v[i]; //每个人说的话 for (int i = 1; i < n; i++){for (int j = i + 1; j <= n; j++){vector<int> lie, a(n + 1, 1); //lie 数组代表说谎的人 ,a 数组代表真实情况 ,好人全赋值为 1 a[i] = a[j] = -1; //i j 为坏人 ,赋值为 -1 for (int k = 1; k <= n; k++)if (v[k] * a[abs(v[k])] < 0) lie.push_back(k); if (lie.size() == 2 && a[lie[0]] + a[lie[1]] == 0){cout << i << " " << j;return 0;}}}cout << "No Solution";return 0;
}

分析:题目:已知 N 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎,说明这两个说谎的人一个是好人一个是狼人,每个人说的数字保存在 v 数组中,i 从 1~n - 1 、j 从 i + 1~n 遍历,分别假设 i 和 j 是狼人,a 数组表示真实情况(该人是狼人还是好人),等于 1 表示是好人,等于 -1 表示是狼人。k 从 1~n 分别判断 k 所说的话是真是假,k 说的话和真实情况不同(即v[k] * a[abs(v[k])] < 0)则表示 k 在说谎,则将 k 放在 lie 数组中,遍历完成后判断 lie 数组,如果说谎人数等于 2 并且这两个说谎的人一个是好人一个是狼人(即a[lie[0]] + a[lie[1]] == 0)表示满足题意,此时输出 i 和 j 并 return,否则最后的时候输出 No Solution


1090 危险品装箱


#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{int n, k, t1, t2;map<int,vector<int>> m; //map 的又一种用法 scanf("%d%d", &n, &k);for (int i = 0; i < n; i++) //map 不止一一对应 {scanf("%d%d", &t1, &t2);m[t1].push_back(t2);m[t2].push_back(t1);}while (k--){int cnt, flag = 0, a[100000] = {0};scanf("%d", &cnt);vector<int> v(cnt);for (int i = 0; i < cnt; i++){scanf("%d", &v[i]);a[v[i]] = 1;}for (int i = 0; i < v.size(); i++)for (int j = 0; j < m[v[i]].size(); j++)if (a[m[v[i]][j]] == 1) flag = 1;printf("%s\n",flag ? "No" :"Yes");}return 0;
}

分析:用 map 存储每一个货物的所有不兼容货物(用到了 map 的新用法),在判断给出的一堆货物是否是相容的时候,判断任一货物的不兼容货物是否在这堆货物中,如果存在不兼容的货物,则这堆货物不能相容,如果遍历完所有的货物,都找不到不兼容的两个货物,则这堆货物就是兼容的


1091 N-自守数

#include <iostream>
#include <string>
using namespace std;
int main()
{int m;cin >> m;while (m--){int k, flag = 0;cin >> k;for (int n = 1; n < 10; n++) //枚举{int mul = n * k * k;string smul = to_string(mul), sk = to_string(k);string smulend = smul.substr(smul.length() - sk.length());if (smulend == sk){printf("%d %d\n", n, mul);flag = 1;break;}}if (flag == 0) printf("No\n");}return 0;
}

题目和代码简单,解析一下 string 类的新用法:substr()函数

string temp;
temp = suf.substr(Off, Count)off 参数: 复制子字符串的起始位置
Count 参数: 复制的字符数目

下面再举例说明:

string suf = "abcde";
string temp,temp2;
temp = suf.substr(2,1);
temp2 = suf.substr(2); //若没有第二个参数则代表一直复制到结尾//下面实例看出是从 0开始计数
output:
c
cde

1093 字符串A+B

#include <iostream>
using namespace std;
int main()
{string s1, s2, s;int hash[200] = {0};getline(cin, s1);getline(cin, s2);s = s1 + s2;for (int i = 0; i < s.size(); i++) //妙招 输出并剔除重复字符{if (hash[s[i]] == 0) cout << s[i];hash[s[i]] = 1;}return 0;
}

分析:因为题目要求若有重复字符先出现 A 的,故做法为:A + B 在剔除重复字符


1095 解码PAT准考证


#include <iostream>
#include <vector>
#include <unordered_map> //更快(仅适用于 PTA)
#include <algorithm>
using namespace std;
struct node
{string t; //1类准考证 3类考场号int value; //1类成绩 3类考场人数
};
bool cmp(const node &a, const node &b) //用引用传参 (速度更快 养成好习惯)
{return a.value != b.value ? a.value > b.value : a.t < b.t;
}
int main()
{int n, k, num;string s;cin >> n >> k;vector<node> v(n);for (int i = 0; i < n; i++)cin >> v[i].t >> v[i].value;for (int i = 1; i <= k; i++){cin >> num >> s;printf("Case %d: %d %s\n", i, num, s.c_str());vector<node> ans; //存储筛选后最终数组 int cnt = 0, sum = 0;if (num == 1) //1类 {for (int j = 0; j < n; j++)if (v[j].t[0] == s[0]) ans.push_back(v[j]); //等级查询 }else if (num == 2) //2类 {for (int j = 0; j < n; j++){if (v[j].t.substr(1, 3) == s) //考场查询 {cnt++;sum += v[j].value;}}if (cnt != 0) printf("%d %d\n", cnt, sum);}else if (num == 3) //3类 {unordered_map<string, int> m;for (int j = 0; j < n; j++)if (v[j].t.substr(4, 6) == s) m[v[j].t.substr(1, 3)]++; //考试日期 考场编号人数 ++ for (auto it : m) ans.push_back({it.first, it.second}); //用 it 遍历 容器 m中的每一个元素 }sort(ans.begin(), ans.end(),cmp); //一个 cmp 全类型通用 for (int j = 0; j < ans.size(); j++)printf("%s %d\n", ans[j].t.c_str(), ans[j].value);if (((num == 1 || num == 3) && ans.size() == 0) || (num == 2 && cnt == 0)) printf("NA\n");}return 0;
}

分析非常有参考价值的分类题

题目大意:给出一组学生的准考证号和成绩,准考证号包含了等级(乙甲顶),考场号,日期,和个人编号信息,并有三种查询方式
查询一:给出考试等级,找出该等级的考生,按照成绩降序,准考证升序排序
查询二:给出考场号,统计该考场的考生数量和总得分
查询三:给出考试日期,查询改日期下所有考场的考试人数,按照人数降序,考场号升序排序

代码:先把所有考生的准考证和分数记录下来
1、按照等级查询,枚举选取匹配的学生,然后排序即可
2、按照考场查询,枚举选取匹配的学生,然后计数、求和
3、按日期查询每个考场人数,用 unordered_map 存储,最后排序汇总

注意:1、第三个用 map 存储会超时,用unordered_map就不会超时啦
2、排序传参建议用引用传参,这样更快,虽然有时候不用引用传参也能通过,但还是尽量用,养成好习惯

解析c_str() 函数的用法

作用:将 C++ 的 string 转化为 C 的字符串数组输出string temp = "123";
printf("%s",temp.c_str());//这样就可以将 temp 以 c语言 的形式输出(目的是为了快)
output:123

补充for (auto it : m) 函数的用法

c++11的新特性,范围 for
m 是一个可遍历的容器或流,比如 vector 类型
it 就用来在遍历过程中获得容器里的每一个元素vector<int> m={1,2,3,4};
for(auto it : v)cout<<it;output: 1234
//注意仅 PTA 适用,蓝桥杯不允许

PTA 乙级难点(全部)相关推荐

  1. PTA乙级1014(python3)

    PTA乙级1014(python3) 题目信息: 大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&a ...

  2. PTA乙级题解(110题全)

    First of all 如果你是你们学校的ACM选手 那么我猜你的目标可能是下面几个吧(^v^) (1) ACM区域赛拿牌 (2) PTA顶级(Top Level) (3) CCF-CSP认证400 ...

  3. 【PTA乙级练习题】topic:1001

    [PTA乙级练习题]topic:1001 题目 审题 代码 知识点 总结 题目 1001 害死人不偿命的(3n+1)猜想 (15 分) 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇 ...

  4. PTA乙级1028 人口普查

    PTA乙级 人口普查 1028 题目 某城镇进行人口普查,得到了全体居民的生日.现请你写个程序,找出镇上最年长和最年轻的人. 这里确保每个输入的日期都是合法的,但不一定是合理的--假设已知镇上没有超过 ...

  5. 牛客网 PTA乙级真题 1003 数素数

    数素数 (20) 时间限制 1000 ms 内存限制 32768 KB 代码长度限制 100 KB 判断程序 Standard (来自 小小) 题目描述 令Pi表示第i个素数.现任给两个正整数M &l ...

  6. PTA 乙级 在霍格沃茨找零钱(c++)

    PTA 乙级 在霍格沃茨找零钱(c++) 题目描述 如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 -- 就如海格告诉哈利的:"十七个银西可(Sickle)兑一个加隆(Galleo ...

  7. PTA 乙级 【1008】 数组元素循环右移问题

    PTA 乙级 [1008] 数组元素循环右移问题 一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置(最后M个数循环移至最前面的M个位置).如果 ...

  8. C语言 之 PTA乙级错误集锦

    1,很大很大的数输入,并各位加和  PTA-1001 #include <stdio.h> #include <math.h> int main(){int sum=0,cou ...

  9. 7-36 社交网络图中结点的“重要性”计算 (30 分)(思路加详解)兄弟们PTA乙级题目冲起来

    一:题目 在社交网络中,个人或单位(结点)之间通过某些关系(边)联系起来.他们受到这些关系的影响,这种影响可以理解为网络中相互连接的结点之间蔓延的一种相互作用,可以增强也可以减弱.而结点根据其所处的位 ...

最新文章

  1. js数组中indexOf/filter/forEach/map/reduce详解
  2. linux查找技巧: find grep xargs amp;amp; linux系统信息查看大全
  3. 宝塔Linux, 反向代理服务器, 开启WSS
  4. H3C S5120-52P-WiNet交换机配置
  5. 【教程】如何在标签打印工具TFORMer Designer中自定义布局?
  6. 字符串、文件操作,英文词频统计预处理
  7. oracle-j2sdk1.8,CDH agent无法安装
  8. mysql(指RDS)验证pureftpd登录
  9. 管理感悟:技术好的人都有点工作狂
  10. 基于微信小程序的毕业设计题目(19)php菜谱美食小程序(含开题报告、任务书、中期报告、答辩PPT、论文模板)
  11. 智邦国际ERP软件实施成功的七大步骤
  12. Android第一行代码踩坑qwq
  13. UVA - 10306 e-Coins
  14. 开学季适合学生党的蓝牙耳机,音质好的蓝牙耳机排行
  15. 禁用eslint / ts相关检查
  16. 第二章第十六题(几何:六边形面积)(Geometry: area of a hexagon)
  17. 1.3 信号处理函数,创建worker进程
  18. 【数据库】数据库的安全性
  19. mongodb aggregate按日期分组统计及spring mongo实现
  20. 超出ipc连接数范围_NVR添加不上IPC,咋整?---大华篇

热门文章

  1. Unhandled promise rejection --解决办法
  2. Django适配达梦数据库
  3. 【Java】子类列表和父类列表能否互相赋值与添加
  4. RK3308设置GPIO的方法
  5. git基本命令与git基本命令-远程
  6. C语言编程单片机相关资料,单片机的C语言编程资料.ppt
  7. Win11打不开安全中心怎么解决
  8. CodeForces 332B Maximum Absurdity
  9. new(创建)一个对象时都发生了什么?
  10. HTML5 新增的结构元素——能用不代表用对了