PAT乙级学习笔记(四)

##续接PAT乙级学习笔记(三)
##以下PAT乙级其它题目
1.B1062最简分数。分数比较的时候可以使用交叉相乘的方法,不要想着使用double类型得到分数的小数形式比较大小。学会使用gcd求最大公倍数的递归写法,当成模板记住。数组定义的时候尽量初始化,不然按照判断数组的数是否为0就会出现错误。以下是代码

#include <iostream>
#include <algorithm>
using namespace std;
int gcd(int a, int b){
    if(b == 0) return a;
    else return gcd(b, a % b);
}
int main(){
    int n1, m1, n2, m2, k;
    scanf("%d/%d %d/%d %d",&n1, &m1, &n2, &m2, &k);
    if(n1 * m2 > n2 * m1){
        swap(n1, n2);
        swap(m1, m2);
    }
    int a[k] = {0};
    int cnt = 0;
    for(int i = 1; i < k; i++){
        if(n1 * k < m1 * i && i * m2 < k * n2 && gcd(k, i) == 1){
            a[i] = i;
            cnt++;
        }
    }
    for(int i = 0; i < k; i++){
        if(a[i] != 0){
            printf("%d/%d",a[i],k);
            if(--cnt > 0) cout << " "; 
        }
    }
    return 0;
}  

注意体会柳神代码中输出空格的写法,相当于第一项不输出空格,其他的每一项都先输出空格再输出数

bool flag = false;
    while(n1 * k >= m1 * num) num++;
    while(n1 * k < m1 * num && m2 * num < n2 * k) {
        if(gcd(num, k) == 1) {
            printf("%s%d/%d", flag == true ? " " : "", num, k);
            flag = true;
        }
        num++;
    }  

2.B1066图像过滤。这道题比较简单,下面是存储到数组中,统一输出的代码

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    int m, n, a, b, c;
    cin >> m >> n >> a >> b >> c;
    int ans[m][n];
    for(int i = 0; i < m; i++){
        for(int j = 0; j < n; j++){
            scanf("%d", &ans[i][j]) ;
            if(ans[i][j] >= a && ans[i][j] <= b){
                ans[i][j] = c;
            }
        }
    }
    for(int i = 0; i < m; i++){
        for(int j = 0; j < n; j++){
            printf("%03d", ans[i][j]);
            if(j != n -1) cout << " ";
        }
        cout << endl;
    }
    return 0;
}  

下面是一边处理一边输出的代码

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    int m, n, a, b, c;
    cin >> m >> n >> a >> b >> c;
    int ans[m][n];
    for(int i = 0; i < m; i++){
        for(int j = 0; j < n; j++){
            scanf("%d", &ans[i][j]) ;
            if(ans[i][j] >= a && ans[i][j] <= b){
                ans[i][j] = c;    
            }
            printf("%03d", ans[i][j]);
            if(j != n -1) cout << " ";
        }
        cout << endl;
    }
    return 0;
}  

3.B1071小赌怡情。较简单,注意游戏结束后要break退出

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    int t0, k;
    cin >> t0 >> k;
    for(int i = 0; i < k; i++){
        int n1, b, t, n2;
        cin >> n1 >> b >> t >> n2;
        if(t0 == 0){
            printf("Game Over.\n");
            break;
        }
        else{
            if(t0 < t) printf("Not enough tokens.  Total = %d.\n",t0);
            else{
                if(b == 0 && n2 < n1 || b == 1 && n2 > n1){
                    t0 = t0 + t;
                    printf("Win %d!  Total = %d.\n",t,t0);
                }
                else{
                    t0 = t0 - t;
                    printf("Lose %d.  Total = %d.\n",t,t0);
                }
            }    
        }
    }
    return 0;
}  

4.B1072开学寄语。属于简单题,注意变量定义的位置。一定要注意“=”和“==”的区别!!!数组初始化如果全部赋值为0可以直接用={0}来赋值,如果赋值为其他数,需要使用循环或者按照本题目中的做法:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int check[10000] = {0};
int main(){
    int n, m, cnt0 = 0, cnt1 = 0;
    cin >> n >> m;
    for(int i = 0; i < m; i++){
        int temp;
        cin >> temp;
        check[temp] = 1;
    }
    for(int i = 0; i < n; i++){
        string name;
        int k,flag = 0;
        cin >> name >> k;
        int ans[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; 
        for(int j = 0; j < k; j++){
            int temp1;
            cin >> temp1;
            if(check[temp1] == 1){
                flag =1;
                ans[j] = temp1;
                cnt1++;
            }
        }
        if(flag == 1){
            cnt0++;
            bool space = false;
            cout << name <<": ";
            for(int j = 0; j < k; j++){
                if(ans[j] != -1){
                    if(space == true) cout << " ";
                    printf("%04d",ans[j]);
                    space = true;
                }
            }
            cout << endl;        
        }
    }
    cout << cnt0 << " " << cnt1;
    return 0;
}  

柳神的代码不太好理解,这里有一个技巧就是,输出名字后,其实每一个违禁品编号前面都有有个空格。对于输出也是只有第一个前面输出名字,其他的都不输出。

#include <iostream>
#include <algorithm>
using namespace std;
bool forbid[10000];
int main() {
    int n, m, temp, k, snum = 0, fnum = 0;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &temp);
        forbid[temp] = true;
    }
    for (int i = 0; i < n; i++) {
        char name[10];
        bool flag = false;
        scanf("%s %d", name, &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &temp);
            if (forbid[temp]) {
                if (!flag) {
                    printf("%s:", name);
                    flag = true;
                }
                printf(" %04d", temp);
                fnum++;
            }
        }
        if (flag) {
            printf("\n");
            snum++;
        }
    }
    printf("%d %d\n", snum, fnum);
    return 0;
}  

5.B1076Wifi密码。这道题相对简单,注意使用scanf以%c的形式是可以读入空格和换行的。使用getline之前必须使用getline来吸收一下。

#include <iostream>
#include <string>
using namespace std;
int main(){
    int n;
    cin >> n;
    getchar();
    string s;
    for(int i = 0; i < n; i++){
        getline(cin, s);
        for(int j = 0; j < s.length(); j++){
            if(s[j] == '-' && s[j + 1] == 'T'){
                cout << s[j - 1] - 'A' + 1; 
            }    
        }    
    } 
    return 0;
}  

注意柳神中while的写法,真的太巧妙了

#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;
}  

6.B1077互评成绩。注意变量定义后的作用范围,这道题vector需要定义在数组以内,如果定义在外面出错,相当于每个循环都初始化一次。四舍五入就是要加上0.5后得到整数。

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int main(){
    int n, m;
    double g1, g2;
    cin >> n >> m;
    for(int i = 0; i < n; i++){
        cin >> g1;
        vector<int> v;
        for(int j = 1; j < n; j++){
            int temp;
            cin >> temp;
            if(temp >= 0 && temp <= m){
                v.push_back(temp);
            }
        }
        int sum = 0;
        sort(v.begin(),v.end());
        for(int i = 1; i < v.size() - 1; i++){
            sum += v[i];
        }    
        double g2 = sum / (v.size() - 2) ;
        cout << (int)((g1 + g2) / 2 + 0.5) << endl;
    }
    return 0;
}  

7.B1087有多少不同的值。是一道简单题,使用set可以大大减少代码量

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int main(){
    int n;
    cin >> n;
    set<int> st;
    for(int i = 0; i <= n; i++){
        int temp = (i / 2) + (i / 3) + (i / 5);
        st.insert(temp);
    }
    cout << st.size();
    return 0;
}  

8.B1020月饼。注意需要按照单价排序,不是按照价格排序。注意是正数还是正正数。需要考虑所有都不满足的情况。下面是代码

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
struct node{
    double total, sumprice,price;
};
bool cmp(node a, node b){
        return a.price > b.price;
}
int main(){
    int n, d;
    cin >> n >> d;
    vector <node> v(n);
    for(int i = 0; i < n; i++) cin >> v[i].total;
    for(int i = 0; i < n; i++) cin >> v[i].sumprice;
    for(int i = 0; i < n; i++){
        v[i].price = v[i].sumprice * 1.0 / v[i].total;
    } 
    sort(v.begin(),v.end(),cmp);
//    for(int i = 0; i < n; i++) cout << v[i].total <<"*"<< v[i].price << endl;
    double sum = 0;
    int i = 0;
    while(d > 0 && i < n){
//        cout << v[i].total << v[i].price << endl;
        if(d >= v[i].total){
            sum += v[i].sumprice;
        }
        else{
            sum += (d * v[i].sumprice * 1.0/ v[i].total);
        }
        d = d - v[i].total;
        i++;
    }
    int tmp = 0;
    for(int i = 0; i < n; i++){
            tmp += v[i].sumprice;
        }
    if(d > tmp) cout << tmp;
    else printf("%.2f", sum);
    return 0;
}  

注意柳神代码中的算法:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct mooncake{
    float mount, price, unit;
};
int cmp(mooncake a, mooncake b) {
    return a.unit > b.unit;
}
int main() {
    int n, need;
    cin >> n >> need;
    vector<mooncake> a(n);
    for (int i = 0; i < n; i++) scanf("%f", &a[i].mount);
    for (int i = 0; i < n; i++) scanf("%f", &a[i].price);
    for (int i = 0; i < n; i++) a[i].unit = a[i].price / a[i].mount;
    sort(a.begin(), a.end(), cmp);
    float result = 0.0;
    for (int i = 0; i < n; i++) {
        if (a[i].mount <= need) {
            result = result + a[i].price;
        } else {
            result = result + a[i].unit * need;
            break;
        }
        need = need - a[i].mount;
    }
    printf("%.2f",result);
    return 0;
}  

9.B1068万绿丛中一点红。这道题参考了柳神的代码,注意容器的resize的用法,为重新指定有效元素的个数,当定义两个变长的二维数组的时候,需要这样定义。vector<vector> v(m)这样定义时表示,其中一个维度已经确定了。
10.B1070结绳。这道题题目读不懂,所以导致做不出来,不过思路是对的,浮点数与正数相加是浮点数。

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    int n;
    cin >> n;
    int a[n];
    for(int i = 0; i < n; i++) cin >> a[i];
    sort(a, a + n);
    double ans = a[0];
    for(int i = 1; i < n; i++){
        ans = (ans + a[i]) / 2;
    }
    printf("%d", (int)ans);
    return 0;
}   

10.B1045快速排序。这道题注意思路。一个数如果按照从小到大的顺序排列后,它的位置和源位置一样,并且大于左边的最大数,这个数就是主元。(另外一种思路是遍历数组,用一个数组来存储这个数左边的最大数,包括自己,遍历右边的数组,用一个数组存储这个数右边的最小数,包括自己,然后再遍历数组后,经过比较,找到主元。)注意当没有主元的时候,为了体现出第二行,需要输出换行符

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int v[100000];
int main(){
    int n;
    scanf("%d", &n);
    vector<int> a(n), b(n);
    for(int i = 0; i < n; i++) {
        scanf("%d",&a[i]);
        b[i] = a[i];
    }
    int max = 0, cnt = 0;
    sort(b.begin(),b.end());
    for(int i = 0; i < n; i++){
        if(a[i] == b[i] && a[i] > max){
            v[cnt++] = a[i]; 
        }
        if(a[i] > max)
            max = a[i];
    } 
    printf("%d\n", cnt);
    for(int i = 0; i < cnt; i++){
        printf("%d", v[i]);
        if(i != cnt - 1) printf(" ");
    }
    printf("\n");
    return 0;
}

11.B1075链表元素分类。这道题相当于一个模板,用node结构体来存储每一个链表结点的数据和下一个地址,另定义为list[100000],list的下标就是地址。输入的时候本结点的地址就相当于是临时变量,存储进list结构体里面。遍历的时候注意使用的是while循环,设定临时指针等于第一个地址(start)当p != -1的时候作为遍历条件,结尾组不要忘记p指向下一个地址。输出时第一个结点只输出本结点地址和数据,其他的结点输出一个本结点地址,换行,再输出一个本结点地址,再输出本结点数据,最后的时候不要忘记输出最后结点的下一个地址为-1.本道题和1025反转链表一个采用顺序存储,一个采用链式存储,需要多注意。

#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);
    }
    int p = start;
    while(p != -1){
        int data = list[p].data;
        if(data < 0)
            v[0].push_back(p);
        else if(data >= 0 && data <= k)
            v[1].push_back(p);
        else
            v[2].push_back(p);
        p = list[p].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;
}