PTA 乙级难点(全部)

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 写出这个数(字符串)

PTA 乙级难点(全部)_第1张图片

#include
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 我要通过!

PTA 乙级难点(全部)_第2张图片
PTA 乙级难点(全部)_第3张图片

#include
#include
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;
                else
                    break;
            if(str[i]=='A')
                if(state==1)
                    state=2;
                else if(state==2)
                    Amiddle++;
                else if(state==0)
                    Afront++;
                else
                    Alater++;
            if(str[i]=='T')
                if(state==2)
                    state=3;
                else
                    break;
        }
        while(Amiddle--)
        {
            Alater-=Afront;
        }
        if(state!=3||Afront!=Alater||i<strlen(str))
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
    return 0;
}

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

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


1004 成绩排名

PTA 乙级难点(全部)_第4张图片

#include
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)猜想

PTA 乙级难点(全部)_第5张图片

#include
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;
        else
            x=(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 数组元素循环右移问题

PTA 乙级难点(全部)_第6张图片

#include
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 说反话

PTA 乙级难点(全部)_第7张图片

#include
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];
        else
            cout<<a[j]<<' ';
    }
    return 0;
}

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


1010 一元多项式求导

PTA 乙级难点(全部)_第8张图片

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

PTA 乙级难点(全部)_第9张图片
在这里插入图片描述

#include
#include  //此头文件也别忘了
using namespace std;
int main()
{
    int cnt,A=0,B=-1,C=0,E=0,B2=0,B3=0;
    double D=0,D2=0; //记得浮点数要用 double
    cin>>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 ";
    else
        cout<<A<<' ';
    if(B3==0)
        cout<<"N ";
    else
        cout<<B2<<' ';
    if(C==0)
        cout<<"N ";
    else
        cout<<C<<' ';
    if(D2==0)
        cout<<"N ";
    else
        cout<<setprecision(1)<<fixed<<D/D2<<' ';
    if(E==0)
        cout<<"N";
    else
        cout<<E;
    return 0;
}

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

补充关于浮点数精度的知识点
PTA 乙级难点(全部)_第10张图片


1015 德才论

PTA 乙级难点(全部)_第11张图片
PTA 乙级难点(全部)_第12张图片

#include 
#include 
#include 
#include 
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

PTA 乙级难点(全部)_第13张图片

#include 
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 组个最小数

PTA 乙级难点(全部)_第14张图片

#include
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 科学计数法

PTA 乙级难点(全部)_第15张图片

#include
#include
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 反转链表

PTA 乙级难点(全部)_第16张图片
PTA 乙级难点(全部)_第17张图片

#include
#include
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 next
	for (int i = 0; i < n; i++) //结点地址,该结点保存的整数数据,下一结点的地址 
	{
		cin >> temp;
		cin >> data[temp] >> next[temp];
	}
	int sum = 0; //真正在链表中的结点个数
	//建立正序地址数组list
	while (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没有存最后一个数,所以要打印 -1
	delete[] data;
	delete[] next;
	return 0;
}

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


1026 程序运行时间

PTA 乙级难点(全部)_第18张图片

#include
#include
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;
	else
		time=(time2-time1)/100;
	int sp=time;
	printf("%02d:%02d:%02d\n",sp/3600,sp%3600/60,sp%3600%60);
	return 0;
}

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


1027 打印沙漏

PTA 乙级难点(全部)_第19张图片

#include
#include 
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 人口普查

PTA 乙级难点(全部)_第20张图片

#include
#include
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;
	else
	cout<<0;
	return 0;
}

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

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


1029 旧键盘

PTA 乙级难点(全部)_第21张图片

#include 
#include 
#include 
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 完美数列

PTA 乙级难点(全部)_第22张图片

#include 
#include 
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
#include
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转换为大写英文字母

两者都包含于头文件


1034 有理数四则运算

PTA 乙级难点(全部)_第23张图片

#include 
#include 
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 插入与归并

PTA 乙级难点(全部)_第24张图片
PTA 乙级难点(全部)_第25张图片

#include 
#include 
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)
PTA 乙级难点(全部)_第26张图片
PTA 乙级难点(全部)_第27张图片
分析:先将 i 指向序列中满足从左到右是从小到大顺序的最后一个下标,再将 j 指向从 i + 1 开始,第一个不满足a[j] == b[j]的下标,如果 j 顺利到达了下标 n,说明是插入排序,否则说明是归并排序

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

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


1039 到底买不买

PTA 乙级难点(全部)_第28张图片
PTA 乙级难点(全部)_第29张图片

#include
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;
	}
	else
		cout<<"No "<<votal1;
    return 0;
}

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


1042 字符统计

PTA 乙级难点(全部)_第30张图片

#include
#include
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转换为小写英文字母

两者都包含于头文件


1044 火星数字

PTA 乙级难点(全部)_第31张图片

#include 
#include 
#include 
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);
		}
        else
        	toearth();
        cout << endl;
    }
    return 0;
}

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

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

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

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

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

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

补充

string s;
getline(cin,s);
cout<<s.size();

//下面实例说明了空格在 getline中也算一个字符
input:a b
output:3

1045 快速排序

PTA 乙级难点(全部)_第32张图片

#include 
#include 
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 数字加密

PTA 乙级难点(全部)_第33张图片

#include 
#include 
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');
    else
        a.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 长,就补齐 b
	b.append(lena - lenb, '0'); //第一个参数是个数,第二个参数是补充的字符
cout << b;

input:12345 123
output:12300

1049 数列的片段和

PTA 乙级难点(全部)_第34张图片

#include 
#include 
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 螺旋矩阵

PTA 乙级难点(全部)_第35张图片

#include 
#include 
#include 
#include 
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 复数乘法

PTA 乙级难点(全部)_第36张图片

#include 
#include 
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);
	else
		printf("%.2lf+%.2lfi",a1,a2);
	return 0;
}

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

double a=0.004;
double b=0.005;
printf("%.2lf\n",a);
printf("%.2lf",b);

//从结果来看,对于格式化,程序会自动四舍五入
output:0.00
	   0.01

1052 卖个萌

PTA 乙级难点(全部)_第37张图片
在这里插入图片描述

#include 
#include 
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 求平均值

PTA 乙级难点(全部)_第38张图片
PTA 乙级难点(全部)_第39张图片

#include 
#include 
#include 
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);
    else
        printf("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 集体照

PTA 乙级难点(全部)_第40张图片
PTA 乙级难点(全部)_第41张图片

#include 
#include 
#include 
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);
        else
            m = 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 组合数的和

PTA 乙级难点(全部)_第42张图片

#include 
#include
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 选择题

PTA 乙级难点(全部)_第43张图片

#include 
#include 
#include 
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];
			else
                wrongCnt[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: 45

input: 45
output: 45

input: )45(
output: 0

1059 C语言竞赛

在这里插入图片描述
PTA 乙级难点(全部)_第44张图片

#include 
#include 
#include 
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");
		else
            printf("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 爱丁顿数

PTA 乙级难点(全部)_第45张图片

#include 
#include 
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 开始,因为天数不能为 0
    sort(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 朋友数

PTA 乙级难点(全部)_第46张图片

#include 
#include 
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 非常好用,但只适用于 PAT

set<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 试密码

PTA 乙级难点(全部)_第47张图片
PTA 乙级难点(全部)_第48张图片

#include 
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 万绿丛中一点红

PTA 乙级难点(全部)_第49张图片
PTA 乙级难点(全部)_第50张图片

#include 
#include 
#include 
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");
    else
        printf("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 1

m[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 微博转发抽奖

PTA 乙级难点(全部)_第51张图片
PTA 乙级难点(全部)_第52张图片

#include 
#include 
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 结绳

PTA 乙级难点(全部)_第53张图片

#include 
#include 
#include 
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 多选题常见计分法

PTA 乙级难点(全部)_第54张图片
PTA 乙级难点(全部)_第55张图片

#include 
#include 
#include 
#include 
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]++; //记录错误次数 
            }
			else
                grade += 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");
	else
        for (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 1100

b = 0000 1101

-----------------

a & b = 0000 1100

a | b = 0011 1101

a ^ b = 0011 0001

~a  = 1100 0011

a << 2 = 1111 0000

a >> 2 = 0000 1111

1074 宇宙无敌加法器

PTA 乙级难点(全部)_第56张图片

#include 
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 链表元素分类

PTA 乙级难点(全部)_第57张图片
PTA 乙级难点(全部)_第58张图片

#include 
#include 
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);
        else
            v[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;
            }
			else
                printf("%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密码

PTA 乙级难点(全部)_第59张图片
PTA 乙级难点(全部)_第60张图片

#include 
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期终成绩

PTA 乙级难点(全部)_第61张图片
PTA 乙级难点(全部)_第62张图片

#include 
#include 
#include 
#include 
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期末 4、将v数组中所有G总满足条件的放入ans数组中,对ans排序(总分递减,总分相同则姓名递增),最后输出ans中的学生信息


1084 外观数列

PTA 乙级难点(全部)_第63张图片

#include 
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;
}
//注意还有头文件 

1085 PAT单位排行

PTA 乙级难点(全部)_第64张图片
在这里插入图片描述

#include 
#include 
#include 
#include 
#include 
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;
    else
        return 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 就不告诉你

PTA 乙级难点(全部)_第65张图片
PTA 乙级难点(全部)_第66张图片

#include 
#include 
#include 
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 狼人杀-简单版

PTA 乙级难点(全部)_第67张图片
PTA 乙级难点(全部)_第68张图片

#include 
#include 
#include 
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 危险品装箱

PTA 乙级难点(全部)_第69张图片
PTA 乙级难点(全部)_第70张图片

#include 
#include 
#include 
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-自守数

PTA 乙级难点(全部)_第71张图片

#include 
#include 
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

PTA 乙级难点(全部)_第72张图片

#include 
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准考证

PTA 乙级难点(全部)_第73张图片
PTA 乙级难点(全部)_第74张图片

#include 
#include 
#include  //更快(仅适用于 PTA) 
#include 
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 适用,蓝桥杯不允许

你可能感兴趣的:(刷题集)