PAT乙级学习笔记(二)

##续接PAT乙级学习笔记(一)
##以下PAT乙级模拟题
1.B1053住房空置率。注意变量的命名方式,更加有助于解题和找bug。注意题目中输出一位小数点百分数的方式,(double)强制类型转换。

double mayvoid1=(double)mayvoid/n*100;
double isvoid1=(double)isvoid/n*100;
printf("%.1f%% %.1f%%",mayvoid1,isvoid1);  

使用%%来输出一个%。使用\来输出一个,对于其他的转义字符需要\来输出,如printf(“\n”)将输出一个\n。
2.B1051复数乘法。当A或B在-0.005到+0.005之间时候,应该输出0.00而不应该输出-0.00,所以在输出的时候需要有一步判断。
3.B1050螺旋矩阵。这道题的思路不是很明确,不知道怎么样输出螺旋矩阵,所以做不出来。柳神给出来的思路很明确,需要借鉴参考学习。
4.B1046划拳。比较简单的一道题,5分钟就搞定了,注意书写变量的命名。
5.B1026程序运行时间。认真体会四舍五入时加上50的写法。注意当其中一项是0的时候应该输出00所以输出格式应该是%02d。输出控制符%.mf是保障精确到小数点后面几位,不是四舍五入,四舍五入应该用round(double)函数。
6.B1018锤子剪子布。题目较简单,注意变量命名的方式,一定要让自己能够看懂。对于每一个人胜利的存储可以参考柳神的方式,用数组,然后找出最大的下标。很巧妙。

int maxjia = jia[0] >= jia[1] ? 0 : 1;
maxjia = jia[maxjia] >= jia[2] ? maxjia : 2;
int maxyi = yi[0] >= yi[1] ? 0 : 1;
maxyi = yi[maxyi] >= yi[2] ? maxyi : 2;
char str[4] = {"BCJ"};
cout << str[maxjia] << " " << str[maxyi];  

7.B1016部分A+B。题目较简单,认为书写函数的方式要好于柳神的方法,代码也比较短。

##以下PAT乙级数学题
1.B1056组合数的和。本题每个输入的数,相当于被加了N-1次,所以可以使用柳神的方式sum += temp * 10 * (N - 1) + temp * (N - 1);当然考场上如果想不起来,也可以使用两层for循环来写,也非常简单。
2.1049数列的片段和。编程不难,数学思想比较难想到,可以通过画图找规律,每一个数实际上都是被加了(n-i)*(n+1)次,通过在纸上演算可以得到。也可以参考柳神的代码用双指针的思想考虑,每个片段的头指针有i种选择,每个尾指针有n-1-i种选择。
3.B1019数字黑洞。这是一道做过的题,注意输入可能不是4位数,计算的过程中可能不是4位数,所以需要两次判断。s.insert(0,4-s.length(),'0');这种插入的用法必须学会。另外当输入为6174的时候也需要进行循环,所以需要使用do语句,不能使用while语句。
##以下是PAT乙级HASH散列问题
1.B1083是否存在相等的数。将相减的数直接作为数组的下标记录个数就行,相对较简单。
2.B1047编程团体赛。本题注意柳神的scanf的应用,比我的方法简单太多了。scanf("%d-%d %d", &t, &num, &score);我是采用读取字符串,然后判断的方法,简直蠢透了。附上我的代码

#include<iostream>
#include<cstdio> 
#include<string>
#include<vector>
#include<cctype>
#include<cmath> 
#include<algorithm>
#include<stack>
#include<set>
using namespace std;
int main(){
int n,sum[1001]={0};
int maxnum,maxscore=0;
string s;
cin>>n;
getchar();
for(int i=0;i<n;i++){
    int com,score=0;
    getline(cin,s);
    for(int j=0;j<s.length();j++){
        if(s[j]=='-'){
            com=stoi(s.substr(0,j));
        }
        if(s[j]==' '){
            score=stoi(s.substr(j+1));
        }    
    }
    sum[com]+=score;
}
for(int i=1;i<=1000;i++){
    if(sum[i]>maxscore){ 
        maxscore=sum[i];
        maxnum=i;
    }
}
cout<<maxnum<<" "<<maxscore;
return 0;
}  

附上柳神的代码:

#include <iostream>
using namespace std;
int main() {
 int n, t, num, score;
 cin >> n;
 int team[1001] = {0};
 for (int i = 1; i <= n; i++) {
 scanf("%d-%d %d", &t, &num, &score);
 team[t] += score;
 }
 int max = 0;
 for (int i = 0; i < 1001; i++) {
 if (team[max] < team[i])
 max = i;
 }
 cout << max << " " << team[max];
 return 0;
}  

3.B1043输出PATest。我的思路是遍历每一个字符,然后如果字符是P就把他放在字符数组的第1个位置,对于下一个P就把它放在第七个位置,但是代码能力太弱,写出来的漏洞百出。以下附上柳神的代码以供学习:

#include<iostream>
#include<cstdio> 
#include<string>
#include<vector>
#include<cctype>
#include<cmath> 
#include<algorithm>
#include<stack>
#include<set>
using namespace std;
int main(){
int map[128]={0},c;\\一共128个ascii码。
while((c=cin.get())!=EOF) map[c]++;
while (map['P'] > 0 || map['A'] > 0 || map['T'] > 0 || map['e'] > 0 ||map['s'] > 0 || map['t'] > 0) {
    if (map['P']-- > 0) cout << 'P';
    if (map['A']-- > 0) cout << 'A';
    if (map['T']-- > 0) cout << 'T';
    if (map['e']-- > 0) cout << 'e';
    if (map['s']-- > 0) cout << 's';
    if (map['t']-- > 0) cout << 't';
 }
 return 0;
}  

4.B1042字符统计。所有变量如果不直接输入都要初始化,这道题如果不初始化t,就会导致测试点2不通过。对于单个字符的hash散列可以使用它的ascii码作为键值。当要求有并列的时候从最小的输出时候,可以从遍历字母的最小的ascii开始。
5.B1039到底买不买。每一个珠子个数都存成a[s[i]-‘0’]的形式,a[]里面对应的不一定是这个字符的ascii码,不过不会影响最后的结果,不过像下面这样也是可以的s[i]默认选取其ascii码。柳神的代码采用了两个for循环,不易想到。

string s1,s2;
cin>>s1>>s2;
for(int i=0;i<s2.length();i++){
    b[s2[i]]++;
}  

6.B1038统计同成绩的学生的个数。相对简单,柳神的代码中使用了vector容器,我是用了数组保存成绩,注意数组的范围是a[101]。
7.B1033旧键盘打印。题目较难,可以参考柳神的代码,尤其注意string::npos的用法,《算法笔记》p207有详细解释:

#include<iostream>
#include <string>
#include<cctype>
#include <map>
using namespace std;
int main(){
string bad,should;
getline(cin,bad);
getline(cin,should);
for(int i=0;i<should.length();i++){
    if(bad.find(toupper(should[i]))!=string::npos) continue;
    if(isupper(should[i])&&bad.find('+')!=string::npos) continue;
    cout<<should[i];
} 
return 0;
}  

8.B1029旧键盘。学会了柳神的find函数,对于判断字母还是大小写处理的还不是很好。

#include<iostream>
#include <string>
#include<cctype>
#include <map>
using namespace std;
int main(){
string s1,s2;
cin>>s1>>s2; 
int a[128]={0};
for(int i=0;i<s1.length();i++){
    if(s2.find(s1[i])==string::npos&&a[s1[i]]==0){
        if(!isalpha(s1[i])){
            a[s1[i]]++;
            cout<<s1[i];
        }else{
            if(islower(s1[i])){
                a[s1[i]-32]++;
                a[s1[i]]++;
                s1[i]=toupper(s1[i]);
                cout<<s1[i];
            }
            else{
                a[s1[i]+32]++;
                a[s1[i]]++;
                cout<<s1[i];    
            }
        }
    } 
} 
return 0;
}    

柳神的代码采用字符串相加的方式,真是妙!

#include <iostream>
#include <cctype>
using namespace std;
int main() {
    string s1, s2, ans;
    cin >> s1 >> s2;
    for (int i = 0; i < s1.length(); i++)
    if (s2.find(s1[i]) == string::npos && ans.find(toupper(s1[i])) ==string::npos)
    ans += toupper(s1[i]);
    cout << ans;
 return 0;
}  

##以下是PAT乙级STL-map题:
1.B1090危险品装箱。这道题比较难,参考了柳神的代码:

#include<iostream>
#include <string>
#include<cctype>
#include<map>
#include<vector>
using namespace std;
int main(){
int n,k,t1,t2;
map<int,vector<int> > m;//每个数关联的数不是确定个,所以使用vector 
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
    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;//将每一个数都设置为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;
        //m[v[i][j]]表示关联的第j个数,a[]表示关联的数存在 
    printf("%s\n",flag ? "No" : "Yes"); 
}
return 0;
}  

2.B1085PAT单位排行。题目不会做,参考了柳神的代码。注意写代码的时候的代码风格,变量和运算符之间需要加空格,结束符,逗号等要靠在前面的变量。scanf(“%lf”,&a)中间是字母l不是数字1.

#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;
else
    return a.school<b.school;
}
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;
}  

3.B1080MOOC期中成绩。这道题和B1085很像,都使用了结构体,自己书写sort的cmp函数,我没做出来,参考的柳神的代码

#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
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;
scanf("%d %d %d",&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;
}

注意输出时,因为名字使用的%s格式,所以输出的时候需要把.name的string格式变为.name.c_str格式,这一点需要注意。
4.B1069微博转发抽奖。本道题自己做出来了,但是思维逻辑有点混乱,参考了柳神的代码。用来做标记的flag可以设置成bool类型false,true。
5.B1063火星数字。参考了柳神的代码,柳神的代码书写真的很规范!

#include <iostream>
#include <string>
using namespace std;
string a[13] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"};
string b[13] = {"####", "tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"};
string s;
int len;
void func1(int t) {
    if (t / 13) cout << b[t / 13];
    if ((t / 13) && (t % 13)) cout << " ";
    if (t % 13 || t == 0) cout << a[t % 13];
}
void func2() {
    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 <= 12; 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 (s[0] >= '0' && s[0] <= '9')
        func1(stoi(s));
    else
        func2();
    cout << endl;
}
return 0;
}    

注意以下的代码风格的规范

void fun1(int x, int y, int z);//良好 
void fun1 (int x,int y,int z); //不良好 

if (year >= 200)    //良好 
if(year>=200)       //不良好 
if ((a>=b) && (a<=d)) //良好 
if(a>=b&&a<=d)       //不良好 

for (i=0; i<10; i++)       //良好风格 
for(i=0;i<10;i++)         //不良好风格 
for (i = 0; i < 10; i++) //过多的空格 

x = a < b ? a : b;  //良好
x=a<b?a:b;          //不良好

int *x=&b;   //良好
int * x=& b; //不良好

6.B1065单身狗。一定要注意“=”和“==”的区别,出现bug第一时间检查是不是这个出问题了!!!写代码的时候可以开大数组,少用STL等,会导致超时!!当输出有0的时候一定要注意输出格式!!这道题自己一点点的做出来了,用了两个多小时:下面是代码

#include<iostream>
#include<unordered_map>
#include<set>
using namespace std;
int sign[100000];
int a[100000]={-1};
int b[100000]={-1};
int main(){
int n, m;
cin >> n;
set<int> ans;
for(int i = 0; i < n; i++){
    int id1, id2;
    scanf("%d %d",&id1,&id2);
    a[id1] = id2;
    b[id2] = id1;
}
cin >> m;
int c[m];
for(int i = 0; i < m; i++){
    scanf("%d",&c[i]);
    if(a[c[i]] == -1 && b[c[i]] == -1) ans.insert(c[i]);//说明是单身来了聚会 
    else sign[c[i]] = 1;//说明不是单身,但是他的另一半有没有来聚会,需要下面判断 
}
for(int i = 0; i < m; i++){//判断不是单身的这些人,另一半来没来 
    if(sign[c[i]] == 1){
        int flag1 = 0;
        for(int j = 0; j < m; j++){
            if(a[c[i]] == c[j] || b[c[i]] == c[j]) flag1 = 1;
        }
        if(flag1 == 0) ans.insert(c[i]);
    }
}
//将单身的按照顺序输出,注意输出空格的方法 
cout << ans.size() << endl;
int space = ans.size();
for(auto it = ans.begin(); it != ans.end(); it++){
    printf("%05d",*it);
    if(space-- > 1) printf(" ");
}
return 0;
}  

7.B1064朋友数。这道题比较简单,注意后面输出空格的方法。另外,stoi只能作用于字符串。