PAT乙级学习笔记(一)

##开一篇文章记录一下学习PAT乙级过程中遇到的问题。

1.string中截取字符串函数s.substr(2,3),表示从s的下标为2的字符开始连续截取3个字符,其中2和3不能为变量
2.B1003 我要通过!map<char,int> mp,当使用mp[‘T’]访问时,返回的是字符T的个数;mp.size()返回的是映射的对数;
3.B1004 成绩排名。当输入中有空格的字符串时,可以直接使用cin>>name>>sum来输入,中间的间隔就是空格。输出空格时cout<<” “,双引号之间一定有空格。
4.B1005 继续3n+1猜想。vector v是两个变长数值,做题时发生了段错误,应该改为vector v[k]表示其中一个是固定长度的。认真体会最后的关于用flag标记来输出中间空格的方法。
5.B1006换个格式输出整数。思维不要僵化,用数组存储各个位的数字,用取余%和整除/。
6.B1007素数对猜想。记住isprime函数的书写方式

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

7.B1008数组元素循环右移。使用vector时必须要添加相应的头文件;vector v(N)注意是小括号,当使用[]时表示vector数组

vector<int> a ;      //声明一个int型向量a
vector<int> a(10) ;   //声明一个初始大小为10的向量
vector<int> a(10, 1) ;    //声明一个初始大小为10且初始值都为1的向量
vector<int> b(a) ;       //声明并用向量a初始化向量b
vector<int> b(a.begin(), a.begin()+3) ;//将a向量中从第0个到第2个(共3个)作为向量b的初始值  

reverse的用法,注意reverse(a,a+4)表示翻转a[0]~a[3];
8.B1010一元多项式求导。认真审题,输出中的0是1-1得0;可以用while(cin>>a>>b)来一直输入;同B1005体会用标记flag来输出空格的方法。
9.B1011A+B大于C。读题读3遍,看好细节;大整数运算时必须使用scanf和printf来输入输出;注意各个类型的取值范围

char -128 ~ +127 (1 Byte)
short -32767 ~ + 32768 (2 Bytes)
unsigned short 0 ~ 65536 (2 Bytes)
int -2147483648 ~ +2147483647 (4 Bytes) 2的31次幂
unsigned int 0 ~ 4294967295 (4 Bytes)
long == int
long long -9223372036854775808 ~ +9223372036854775807 (8 Bytes)
double 1.7 * 10^308 (8 Bytes)
unsigned int 0~4294967295
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:184467440737095516  

10.B1012数字分类。用scanf进行输入时,必须使用&,否则会出现段错误,避免段错误有,大数组必须定义在全局变量,数组开小了,sort函数排序出问题。当n1-n2+n3-n4…时可以在每一层循环中加入符号变量s=s*(-1),使用头文件中的pow(double n1,double n2)中注意double类型。当输出空格时可以循环printf(” “),也可以直接输出printf,也可以直接printf(“N “);不存在和等于0不是相等的,所以A2的测试点8一直通不过,这道题简直太精辟了;
11.B1013数素数。按照每行10个的输出方式是用循环,注意前后顺序,判断和打印不能颠倒。代码背下来,:

cnt=0;
for(int i=0;i<v.size();i++){
    cnt++;
    if(cnt%10!=1) printf(" ");
    printf("%d",v[i]);
    if(cnt%10==0) printf("\n");
}  

写判断是否为素数的bool函数,要注意括号的位置:

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

12.B1014福尔摩斯的约会。星期隐含着要A~G。输出的星期要是字符串,时间要是整型。时间注意前面要补零。找到相同的字符串后要跳出。判断分钟的时候用for条件语句时可以使用中间两个条件,当使用if判断条件时,必须在一个if里面全包括如下,如果去掉((s1[j]>='A'&&s1[j]<='N')||isdigit(s1[j])) 就会导致测试点1和2错误。

for(int j=k+1;j<s1.size()&&j<s2.size();j++){
        if(s1[j]==s2[j]&&((s1[j]>='A'&&s1[j]<='N')||isdigit(s1[j]))){
            if(isdigit(s1[j])) printf("%02d:",s1[j]-'0');
            if(s1[j]>='A'&&s1[j]<='N') printf("%d:",s1[j]-'A'+10);
            break;
        }
    }。  

ctype中相关的函数(都是判断一个字符,不能判断字符串)以下结果如果是,就返回设定值,如果不是就返回原来的值,所以可以直接用,不用判断是否为大写字母等,如果是就tolower例子见B1029.

isalnum        是否为字母数字
isalpha        是否为字母
islower        是否为小写字母
isupper        是否为大写字母
isdigit        是否为数字
isxdigit    是否为16进制数字
iscntrl        是否为控制字符
isgraph        是否为图形字符(例如,空格、控制字符都不是)
isspace        是否为空格字符(包括制表符、回车符、换行符等)
isblank        是否为空白字符(C99/C++11新增)(包括水平制表符)
isprint        是否为可打印字符
ispunct        是否为标点
tolower        转换为小写
toupper        转换为大写

这道题是一道文字游戏题,一定要认真审题后再下笔。

##以下是乙级字符串处理的题:
1.B1021个位数统计。比较简单,vector a[10]表示建立一个vector数组,a[0]~a[9]每一个都是一个vector容器,vector常见用法如下:

(1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
(2)a.assign(4,2); //是a只含4个元素,且每个元素为2
(3)a.back(); //返回a的最后一个元素
(4)a.front(); //返回a的第一个元素
(5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
(6)a.clear(); //清空a中的元素
(7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
(8)a.pop_back(); //删除a向量的最后一个元素
(9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+         3(不包括它)
(10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
(11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
(12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
(13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8         ,插入元素后为1,4,5,9,2,3,4,5,9,8
(14)a.size(); //返回a中元素的个数;
(15)a.capacity(); //返回a在内存中总共可以容纳的元素个数
(16)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
(17)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
(18)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才         显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能) 
(19)a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
(20)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<  

2.B1024科学技术法。寻找字符串中某一个字符的位置可以用int i=0;while(s[i]!='E') i++;。截取字符串和截取字符串转换为数字可以使用string t=s.substr(1,i-1);//这是E前面的字符串 //int n=stoi(s.substr(i+1));//从i+1开始一直截取到最后//.注意体会一个for循环中有两对条件的方法。
3.B1031查验身份证。当出现有判断时,先考虑是否采用函数,方程更简单。学会string每一位校验的时候,出现特殊情况怎么处理,将它剔除来单独处理。int tmp=(s[17]=='X') ? 10 : (s[17]-'0');return b[num%11]==tmp;这个返回写的太棒了。cin输入结束是以空格结束的。用flag来标记是否已经输出的方法继续学习。
4.B1048数字加密。A的长度大于B时,加不加0需要商讨。学会用字符串反转,reverse(a.begin(),a.end());a为字符串。学会使用append用法。b.append(lena-lenb,'0');表示在字符b的结尾添加lena-lena长度的字符‘0’。使用append()添加文本常用方法:直接添加另一个完整的字符串:如str1.appen(str2);
添加另一个字符串的某一段子串:如str1.append(str2, 11, 7);
添加几个相同的字符:如str1.append(5, ‘.’);。用字符串来存储加密之后的结果c+=str[(a[i]-'0'+b[i]-'0')%13];。定义字符数组用char str[14]={"0123456789JQK"};访问时可以直接使用str[n]来访问。
5.B1052卖个萌。break只能跳出一层循环,只能用于do~while,for,while语句,不能用于if语句,continue语句表示本次循环体中余下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为仅结束本次循环,continue语句并没有使整个循环终止。vector<vector<string> >v。相当于二维可变长度数组。\表示转义字符,当想输出时必须再加上\即\。一行内有空格时必须使用string s;getline(cin,s);来读入一行。注意变量的定义位置,当把vector<string> row;定义在三层循环外面的时候会出现答案错误。中间读取字符的时候多用while语句,类似于一个双指针的操作,十分精彩。贴上大神的代码:

#include<iostream>
#include<string>
#include <algorithm>
#include<vector>
using namespace std;
int main(){
vector<vector<string> > v;
for(int i=0;i<3;i++){
    string s;
    getline(cin,s);
    int j=0,k=0;
    vector<string> row;
    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,a,b,c,d,e;
cin>>n;
for(int i=0;i<n;i++){
    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;    
}    

6.B1054求平均值。当输入有不同字符时,可以采用sscanf和sprintf来实现不同格式的输入输出scanf("%s", a);sscanf(a,"%lf",&temp);sprintf(b,"%.2f",temp);其中a和b为字符数组。scanf结束的几种方式,空格或跳格,超过指定宽度,当非法输入。判断是否是非法字符的方式如下,用读入两位小数的字符数组长度和原来的字符数组长度长度来计算:方法很巧妙。

for(int j=0;j<strlen(a);j++){
        if(a[j]!=b[j]) flag=1;
        if(flag||tmp<-1000||tmp>1000){
            printf("ERROR: %s is not a legal number\n",a);
            continue;
        }
        else{
            sum+=tmp;
            cnt++;
        }
    }  

7.B1067试密码。PAToj中输入和输出可以一起输出,也可以单次输出。用getchar()来吸收后面的换行”\n”。getline(cin,s)可以一次输入一整行。做好题目的分析,最后先打好流程图。
8.B1081检查密码。非空字符串也可以是空格,所以不能用cin来读入,需要使用getline(cin,s)来读入一行。第一行读入之后要求将最后回车键吸收掉getchar().对于PAT考试,“对于有多组测试数据的输入,可以全部读入之后再输出,也可以处理一组测试数据就输出一组”,详见PAT运行说明。
9.B1084外观数列。当定义二维vector数组时,vector<vector >v(45)后面尽量写上一个维的大小,否则容易容器下标越界。本题是采用双指针的方法,认真体会指针回溯的过程:

for(int i=0;i<s.length();**i=j**){
        for(j=i;j<s.length()&&s[j]==s[i];j++);
        t+=s[i]+to_string(j-i);    
    }  

10.B1086就不告诉你。输出要是数字,使用stoi()函数来实现。将数字反转的时候采用reverse(s.begin(),s.end())来实现。
11.B1058选择题。注意变量的命名方式一定要规范,绝对不能出现拼音。用vector<set >rigtht(m)来存储每道题的正确答案可以保证一定是按照字母顺序输入的,同时使用st来存储学生的答案。注意本题当中所使用的scanf(“ %c”,&c)的用法,当以%c的形式来读入时是可以读入空格的。

##以下是PAT乙级的逻辑题
1.B1084三人行。double c=2*1.0/5,cout<<c;等于0.4。double c=2/5,cout<<c;等于0。double(2/5)表示先进行除法,再进行强制类型转换。注意输出格式是一行内还是多行。本题的输出可以用一个函数:
void print(double t) {
if (m == t) printf(“ Ping”);
else if (m < t) printf(“ Cong”);
else printf(“ Gai”);
}
注意abs(a-b)的使用,返回的是int类型,fabs返回的是double类型。一个两位数的倒着输出可以用 = i % 10 * 10 + i / 10;同样的三位数也适用。
2.B1082射击比赛。可以采用下的MAX()来取最大值。本题是一道先假设最大和最小之后依次替换的典型题目,注意当n=0时,即最大和最小都是一个人的时候。
3.B1079延迟的回文数。大整数计算的写法:

string add(string s1,string s2){
string s=s1;
int carry=0;
for(int i=s1.length()-1;i>=0;i--){
    s[i]=(s1[i]-'0'+s2[i]-'0'+carry)%10+'0';//字符串先变成数字进行运算再加上'0'变成字符。 
    carry=(s1[i]-'0'+s2[i]-'0'+carry)/10;
}
if(carry>0) s='1'+s;
return s;
}  

注意reverse是一个函数的过程,不能够赋值,string s=reverse(a.begin(),a.end())这样的写法会出现问题;判断是否是回文数字的时候采用的是函数内写reverse()的方法。主函数当中如果出现return 0;就表示直接退出主函数,程序结束运行。
4.B1078字符串的压缩与解压。当压缩的时候也可以不采用双指针的方式,也可以采用单指针加上while循环的形式。注意解压的时候数字不一定是一位数,也可能是多位数,认真体会下面的统计位数的函数。

#include<iostream>
#include<string>
#include<vector>
#include<cctype>
#include<cmath> 
#include<algorithm>
using namespace std;
void c(string s){
int j=0;
for(int i=0;i<s.length();i++){
    int cnt=0;
    while(s[i]==s[i+1])
        i++,cnt++; 
    if(cnt!=0) cout<<cnt+1;
    cout<<s[i];  
}    
}
void d(string s){
for(int i=0;i<s.length();i++){
    int sum=0;
    while(isdigit(s[i]))
        sum=sum*10+s[i++]-'0';
    for(int j=0;j<sum;j++)
        cout<<s[i];
    if(sum==0)
        cout<<s[i];
}
}
int main(){
string s,s0;
cin>>s0;
getchar();
getline(cin,s);
if(s0=="C") c(s);
if(s0=="D") d(s); 
return 0;    
}

5.B1074宇宙无敌加法器。当从字符串最后一项是是s[s.length()-1]。注意“==”的书写。自定义函数当中变量的改变不会影响主函数中变量的改变。注意本道题中s(进制数字符)需要初始化,算法中直接赋值了。加法器定义的array必须定义在循环的外侧。加法器每一次只能够进1位。如何使用flag只标记一次,本题给出了if的方法。

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

当两个字符串需要补同样长的时候,除了使用,reverse()反转再使用append()在字符后加0之外,也可以采取本题的方式,很巧妙。

string ss1(s.length() - s1.length(), '0');
 s1 = ss1 + s1;
string ss2(s.length() - s2.length(), '0');
s2 = ss2 + s2;  

6.B1060爱丁顿数。本题的大数组定义在了函数内部也没有超时,但是时间明显增加了很多,为了保险起见,大数组还是要开全局。数组的下标可以从1开始,不一定要从0开始,注意使用的时候也是从下标1开始的如sort。当书写sort的比较函数的时候,为了产生冲突,把比较函数定义为cmp1。本题有一个测试点没做出来,因为题意理解不明白…..
7.B1059C语言竞赛。注意模块化编程的思想,分步骤完成每一个功能。当出现要更改的时候,一定要把所有的地方都改到,本题就是把cout输出改为printf输出的时候有一个地方没改到导致测试点2通过不了。注意体会柳神代码中,使用set来标记是否输出的方法,set可以自动去掉查找的过程中重复的id插入:

if(ss.find(id) == ss.end()) //表示没有找到id值就插入
ss.insert(id);
} else {
printf("Checked\n");
continue;
}  

8.B1063输出谱半径。注意printf函数中%md,%0md,%.mf这三种用法。
9.B1040有多少个PAT.本题如果暴力求解(就是使用三层for循环)会导致运算结果超时。本题的核心是先找A,找到A 就把结果更新,对于每一个A,它的左侧的P个数和右侧的T个数乘积就是这个A对应的PAT,所有结果相加就得到最后结果。这里有一个技巧是先遍历一遍字符串,求出T的个数,然后,每次判断如果为T 就把T个数减1.注意更新结果的时候每一次都对1000000007取余,结果更小。