PAT (Basic Level) Practice 1001~1022

PTA Basic Level Practice 解题思路和代码,主要用的是 C++。每22题一篇博客,可以按目录来进行寻找。


1001 害死人不偿命的(3n+1)猜想

思路:

简单的判断+循环,用一个三目运算符即可。

#include <iostream>
using namespace std;

int main()
{
    int n, count = 0;   // count 为步数
    cin >> n;
    while (n != 1)      // n 为1时退出循环
    {
        n = n % 2 == 0 ? (n / 2) : ((3 * n + 1) / 2);
        ++count;
    }
    cout << count;
    
    return 0;
}

1002 写出这个数

思路:

100位的整数,用哪种内置类型都表示不了,所以输入的一定是整数字符串。题目说 n 小于 1 0 100 10^{100} 10100,表明其最多只有一百位,假设每一位都是最大的9,100个9相加都只有900。所以 n 转换成数字最大也就是999。所以只需要判断三个位置上的数字即可:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string n;
    int num = 0;
    string cn[10] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"};
    cin >> n;
    for (int i = 0; i < n.size(); ++i)
        num += n[i] - '0';

    if (num / 100) cout << cn[num / 100] << " ";        // num / 100 得到的是百位数
    if (num / 10) cout << cn[(num % 100) / 10] << " ";  // (num % 100) / 10 得到的是十位数
    cout << cn[num % 10];                               // 个位不用判断直接输出

    return 0;
}

1003 我要通过!

思路:

第二点:任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串。说明形如 PATAPATAAAPATAA 都是正确的,即 PAT 两侧字符 A 的数量要一样多。

第三点:如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a、 b、 c 均或者是空字符串,或者是仅由字母 A 组成的字符串。推到一下:

  • 因为 PAT 是正确的,此时 ac 均为空串,b 为 A,所以 PAATPAAAT 也是正确的。以此类推中间加多少个 A 都是正确的。
  • 因为 APATA 是正确的,此时 abc 均为 A,所以 APAATAA 也是正确的。
  • 因为 APAATAA 是正确的,此时 a 为 A,b 为 AA,c 为 AA,所以 APAAATAAA 也是正确的,以此类推,只要 P 左边是1个 A 且 T 的左右 A 的数量相同都是正确的。
  • 因为 AAPATAA 是正确的,此时 ac 均为 AA,b 为 A,所以 AAPAATAAAA 也是正确的,再推一下 AAPAAATAAAAAA 也是正确的。

推了这几轮就能发现,正确的式子必须满足三点:

  • 只有一个 P 和一个 T,P 在 T 的左边且中间必须有 A。
  • 不能含有除 PAT 外的任何字符。
  • (P 左侧的 A 的数量) * (P 和 T 中间 A 的数量) = T 右侧 A的数量。

使用 map 容器来保存每次字符出现的次数,在枚举字符的过程中,每次遇到 P 或 T 都将位置保存在相应的变量中。最后的 if 语句中的条件解释如下。

  • mp[‘P’] == 1 && mp[‘T’] == 1:只有一个 P 和一个 T;
  • t - p > 1:T 的位置必须在 P的右侧;
  • mp[‘A’] != 0:必须含有字符 A;
  • mp.size() == 3:如果出现过其他字符,map 的大小就会大于3,该条件确保只出现过 P、A、T 三个字符;
  • p * (t - p - 1) == s.size():(P 左侧 A 的数量) * (P 和 T 中间 A 的数量) = T 右侧 A 的数量。
#include<iostream>
#include<map>
using namespace std;

int main()
{
    int n, p = 0, t = 0;    // p 保存 'P' 的位置,t 保存 'T' 出现的位置
    cin >> n;
    string s;
    while (n--)
    {
        cin >> s;
        map<char, int> mp;
        for (int i = 0; i < s.size(); ++i)
        {
            ++mp[s[i]];     // 当前字符个数加1
            if (s[i] == 'P') p = i; 
            if (s[i] == 'T') t = i; 
        }
        if (mp['P'] == 1 && mp['T'] == 1 && t - p > 1 && mp['A'] != 0 
            && mp.size() == 3 && p * (t - p - 1) == s.size() - t - 1)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }

    return 0;
}

1004 成绩排名

#include<iostream>
#include<map>
using namespace std;

int main()
{
    int n, score, score_max = -1, score_min = 101;
    string name, id, name_max, name_min, id_max, id_min;
    cin >> n;
    while (n--)
    {
        cin >> name >> id >> score;
        if (score > score_max)  // 更新最高分的姓名和学号
        {
            score_max = score;
            name_max = name;
            id_max = id;
        }
        if (score < score_min)  // 更新最低分的姓名和学号
        {
            score_min = score;
            name_min = name;
            id_min = id;
        }
    }
    cout << name_max << " " << id_max << endl;
    cout << name_min << " " << id_min;

    return 0;
}

1005 继续 (3n+1) 猜想

思路:

将输入的所有整数全部递推一遍,可以发现题目意思是:输出没有出现在递推过程中的所有数字,也就是没有被其他数字覆盖的数,而且得从大到小输出。可以先根据输入的整数建立一个 map<int, int>,键是数,值表示其是否出现过。值为 0 表示未出现过,为 1 表示出现过。然后遍历 map,对所有键进行一次 3n + 1 递推,这个过程中出现过的所有数字,如果在 map 中没有这个键就添加且令值为1,有则不管。最后打印所有值为 0 的键,也就是关键数字。

  • 由于 map 容器会自动从小到大排序,所以从尾元素往首元素遍历。
  • 输出时注意空格的处理。
#include <iostream>
#include <map>
using namespace std;

int main()
{
    map<int, int> hash; // 键:值————数字:是否出现过
    int k, n;
    cin >> k;
    while (k--)         // 循环输入 k 个正整数
    {
        cin >> n;
        hash[n] = 0;    // 初始值设为0,表明尚未被覆盖
    }
    for (auto it = hash.cbegin(); it != hash.cend(); ++it)
    {   // 遍历 map 容器
        int temp = it->first;           // temp 保存键
        while (temp != 1)               // temp 为1时退出循环
        {
            temp = temp % 2 == 0 ? (temp / 2) : ((3 * temp + 1) / 2);
            hash[temp] = 1;             // 递推过程出现的数都是被覆盖的数,将其值设为0
        }
    }

    auto it = hash.crbegin();           // it 是指向尾元素的常迭代器
    while (it->second) ++it;            // it 向容器首元素方向移动直到遇见第一个关键数字
    cout << it++->first;                // 打印第一个关键数字,后面不跟空格
    for (; it != hash.crend(); ++it)    // 打印剩余的所有关键数字,其前面都需要加上空格
        if (!it->second)
            cout << " " << it->first;

    return 0;
}

1006 换个格式输出整数

#include<iostream>
#include<map>
using namespace std;

int main()
{
    int n, i;
    cin >> n;
    int h = n / 100, t = n % 100 / 10, u = n % 10;  // h 百位数    t 十位数    u 个位数
    for (i = 0; i < h; ++i) cout << 'B';            // 打印字符 B
    for (i = 0; i < t; ++i) cout << 'S';            // 打印字符 S
    for (i = 1; i <= u; ++i) cout << i;             // 打印个位数

    return 0;
}

1007 素数对猜想

思路:

对于素数算法不了解的读者可以阅读这篇文章:OJ 刷题必备知识总结(二)知识点26。在本题中,每次发现一个素数,就判断一下其与前一个素数的差值,符合题目要求计数就+1。

#include<iostream>
using namespace std;

int main()
{
    int n, pre = 2, count = 0;
    cin >> n;
    for (int i = 2; i <= n; ++i)
    {
        int flag = 1;       // flag 为1表明 i 是素数
        for (int j = 2; j * j <= i; ++j)
            if (i % j == 0)
                flag = 0;   // 发现其他因子,i 不是素数
        if (flag)
        {
            if (i - pre == 2)   // 当前素数与前一个素数差为2
                ++count;
            pre = i;            // pre 记录前一个素数
        }
    }
    cout << count;

    return 0;
}

1008 数组元素循环右移问题

思路:

读者可以手写题给数组平移前后的元素下标对比,就可以发现其中的规律。假设初始下标应为 a,平移后的为 b,则有:
b = ( a + m ) % n b = (a + m) \% n b=(a+m)%n

取余是因为 m 有可能大于 n(第三个测试点)。

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    int n, m, k;
    cin >> n >> m;
    vector<int> vec(n, 0);         // 定义大小为0的数组,初始值全0
    for (int i = 0; i < n; ++i)
    {
        cin >> k;
        vec[(i + m)% n] = k;
    }

    for (int i = 0; i < n - 1; ++i) // 打印整数序列
        cout << vec[i] << ' ';
    cout << vec[n - 1];            // 最后一个整数单独打印

    return 0;
}

1009 说反话

思路:

使用 while (cin >> word) 是没问题的, 因为 PAT 的输入样例都是通过文件来读取的,读到文件结尾输入流就停止了。

#include<iostream>
#include<stack>
using namespace std;

int main()
{
    stack<string> st;
    string word;
    while(cin >> word)              // 循环读入每一个单词
        st.push(word);              // 单词入栈

    if (!st.empty())                // 打印栈顶单词之前必须检查栈是否为空
        cout << st.top();           // 打印栈顶单词
    st.pop();                       // 栈顶单词出栈
    while(!st.empty())              // 栈空停止循环
    {
        cout << " " << st.top();
        st.pop();                   // 栈顶单词出栈
    }

    return 0;
}


1010 一元多项式求导

思路:

题设的一个陷阱是,如果多项式只有常数项,则应当输出 “0 0”;但如果不只有常数项,则不管常数项出现在多项式的哪个位置,求导后都不进行输出(直接跳过)。

例如 3 + 4 x − 1 + 5 x − 2 3 + 4x^{-1} +5x^{-2} 3+4x1+5x2,输入为3 0 4 -1 5 -2,那么求导后是 − 4 x − 2 − 10 x − 3 -4x^{-2} - 10x^{-3} 4x210x3,输出应该是 -4 -2 -10 -3,开头的 “0 0” 不要打印。

关键在于 flag 的定义,一举两得,既能拿来判定输出的格式,还能保证在没有输出的时候能正确输出 “0 0”。

#include <iostream>
using namespace std;

int main()
{
    int n, k, flag = 0;             // n 为系数,k 为指数,flag 为1表明有过输出
    while (cin >> n >> k)
    {
        if (k != 0)                 // 非常数项才输出求导后的数字
        {
            if (flag) cout << " ";  // 前面有输出过数字,则需要先打印一个空格
            cout << n * k << " " << k - 1;
            flag = 1;               // 置 flag 为1表明有过输出
        }
    }
    if (!flag) cout << "0 0";       // 如果没有输出过,打印 “0 0”

    return 0;
}


1011 A+B 和 C

思路:

int 型数据能表示的范围是 [ − 2 − 31 , 2 31 − 1 ] [-2^{-31}, 2^{31} - 1] [231,2311],所以 a + b 有可能超过 int 型数据所能表示的最大范围,要用 long int 来定义三个变量。

#include <iostream>
using namespace std;

int main()
{
    long int a, b, c, t;
    cin >> t;
    for (int i = 1; i <= t; ++i)
    {
        cin >> a >> b >> c;
        if (a + b > c) cout << "Case #" << i << ": true" << endl;
        else cout << "Case #" << i << ": false" << endl;
    }

    return 0;
}


1012 数字分类

思路:

题目不难,多写几个 if else 也能达到题目的要求。难点在于如何利用 C++ 的知识来解决。我采用了柳神的想法,根据余数将数字添加到对应的数组中,再集中进行处理。代码中要注意的一点是除5余1的情况,因为 j 是从0开始的,除2余数为0是加,除2余数为1才是减。

#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;

int main()
{
    vector<int> vec[5];     // 定义一个二维数组
    int n, k, sum1 = 0, sum2 = 0, sum3 = 0, max = 0;
    cin >> n;
    while (n--)             // 循环输入 n 个数
    {
        cin >> k;
        vec[k % 5].push_back(k);        // 根据余数添加到对应的数组
    }
    for (int i = 0; i < 5; ++i)         // 枚举二维数组中的所有数字
    {
        for (int j = 0; j < vec[i].size(); ++j)
        {
            if (i == 0 && vec[i][j] % 2 == 0) sum1 += vec[i][j];    // 求出除5余2的所有偶数的和
            if (i == 1) sum2 += (j % 2 ? -1 : 1) * vec[i][j];       // j 从0开始的,所以要注意判定条件
            if (i == 3) sum3 += vec[i][j];                          // 求出除5余3的所有数的和
            if (i == 4 && vec[i][j] > max) max = vec[i][j];         // 求出除5余4的数字中的最大值
        }
    }

    for (int i = 0; i < 5; ++i)
    {
        if (i != 0) cout << " ";        // A1 前不输出空格
        if (i == 0 && sum1 == 0 || i != 0 && vec[i].size() == 0) cout << "N";   // 分类中不存在数字,输出 N
        else
        {
            if (i == 0) cout << sum1;
            if (i == 1) cout << sum2;
            if (i == 2) cout << vec[2].size();
            if (i == 3) cout << setiosflags(ios::fixed) << setprecision(1) << (sum3 * 1.0) / vec[3].size();    
            if (i == 4) cout << max;
        }
    }

    return 0;
}

1013 数素数

思路:

这一题不能按照常规思路:找出某个范围内的所有素数,然后再去输出 P M P_M PM P N P_N PN 之间的素数。因为 N 或许会很大从而出现段错误;如果直接定义一个很大的范围寻找素数还可能会导致超时,并且也并不清楚第10000个素数究竟是到多大了。

不妨这么做:从 i = 2 开始枚举所有数字,每一轮都判断 i 是否是素数,用变量 count 来标记 i 是第几个素数,一直找到 count = N - 1 时停下来,只打印 count >= M 后的所有素数。

#include <iostream>
using namespace std;

int main()
{
    int m, n, count = 0;                    // count 统计素数的个数
    cin >> m >> n;
    for (int i = 2; count < n; ++i)         // 因为 count 从0开始递增,所以要小于 n
    {
        int flag = 1;                       // flag 为1表明 i 是素数
        for (int j = 2; j * j <= i; ++j)    // 判断 i 是否为素数
            if (i % j == 0) flag = 0;       // 发现其他因子,i 不是素数
        if (flag) ++count;                  // count 计数值+1
        if (count >= m && flag)             // 如果 count 比 m 大且 i 是素数就需要输出
        {
            if ((count - m + 1) % 10 != 1) cout << " ";     // 每行的第一个数字前没有空格
            cout << i;
            if ((count - m + 1) % 10 == 0) cout << endl;    // 每行的最后一个数字后要换行
        }
    }

    return 0;
}

1014 福尔摩斯的约会

思路:

  • 注意小时和分钟的输出格式。
  • 这里如果你图省事儿可以混用 cin 和 printf(printf 控制输出格式更简单),这样子是可以的。但是最好不要 cout 和 printf 混用,cin 和 scanf 混用。
#include <iostream>
#include <cctype>
#include <iomanip>
using namespace std;

int main()
{
    string s[7] = {"MON","TUE","WED","THU","FRI","SAT","SUN"}, s1, s2, s3, s4;
    cin >> s1 >> s2 >> s3 >> s4;
    int i = 0;
    for (; i < s1.size() && i < s2.size(); ++i) // 寻找前面两个字符串中第一对相同的大写字母
    {
        if (s1[i] == s2[i] && 'A' <= s1[i] && s1[i] <= 'G')
        {
            cout << s[s1[i] - 'A'] << " ";      // 不要忘记输出后面的空格
            break;                              // 退出循环
        }
    }
    ++i;
    for (; i < s1.size() && i < s2.size(); ++i) // 寻找前面两个字符串中第二对相同的大写字母或数字字符
    {
        if (s1[i] == s2[i] && (isdigit(s1[i]) || 'A' <= s1[i] && s1[i] <= 'N'))
        {   // setw 指定输出的宽度为 w 个字符,setfill 指定输出宽度不足时的填充字符 c
            if (isdigit(s1[i])) cout << setw(2) << setfill('0') << s1[i] - '0' << ":"; // 数字字符与大写字母的输出不一样,要区分
            else cout << setw(2) << setfill('0') << s1[i] - 'A' + 10 << ":";
            break;
        }
    }
    for (i = 0; i < s3.size() && i < s4.size(); ++i)
    {
        if (s3[i] == s4[i] && isalpha(s3[i]))
        {
            cout << setw(2) << setfill('0') << i;
            break;
        }
    }

    return 0;
}

1015 德才论

思路:

这一题一定要多读几遍,然后在草稿纸上写出分类的标准。先思考出一个最初的解决方案,再一步步地进行优化。代码主要分两个部分,一个部分是根据成绩区分四类考生,另一个部分是根据成绩进行排序

最开始我的想法是统一存入一个 vector 中,然后利用 sort 来排序。但是发现在 compare 函数中就需要写很多的代码:不仅要区分四类考生,在每类考生下又进行排序。而在 main 函数中我还是要做同样的工作,所以我索性把排序和分类彻底分开。将排序提取出来,让 compare 函数只负责排序。然后只在 main 函数中进行分类,在输入数据的时候就直接按照分数分入不同的 vector 中,输出时对每个 vector 应用 sort 后再输出即可。

经验总结:

  • 先判断有没有过线,再进行考生分类,可以缩减语句,否则每个判断中都需要判断是否过线。

最后的 AC 代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct stu
{
    int id, d_score, c_score, t_score;  // 准考证号、德分、才分、总分
    stu(int id, int d_score, int c_score)
    {
        this->id = id;
        this->d_score = d_score;
        this->c_score = c_score;
        this->t_score = d_score + c_score;
    }
};

vector<stu> vec[4];     // 定义二维数组
int m, l, h, d, c, i;

bool compare(struct stu p, struct stu q)
{
    if (p.t_score != q.t_score) return p.t_score > q.t_score;           // 总分不相同时,总分高的排在前面
    else if (p.d_score != q.d_score) return p.d_score > q.d_score;      // 总分相同时,德分更高的排在前面
    else return p.id < q.id;    // 总分和德分相同的话按照准考证号升序排列
}

int main()
{
    cin >> m >> l >> h;
    while (m--)
    {
        cin >> i >> d >> c;
        struct stu temp(i, d, c);
        if (d >=h && c >= h) vec[0].push_back(temp); // 第一类考生:“才德全尽”
        else if (d >= h &&  l <= c && c < h) vec[1].push_back(temp);    // 第二类考生:“德胜才”
        else if (l <= d && d < h && l <= c && c < h && d >= c)
            vec[2].push_back(temp); // 第三类考生:“才德兼亡”但尚有“德胜才”
        else if (l <= d && d < h && l <= c) vec[3].push_back(temp);     // 第四类考生
    }
    for (int i = 0; i < 4; ++i)
        sort(vec[i].begin(), vec[i].end(), compare);
    cout << vec[0].size() + vec[1].size() + vec[2].size() + vec[3].size() << endl;
    for (int i = 0; i < 4; ++i)
    {
        for (int j = 0; j < vec[i].size(); ++j)
            cout << vec[i][j].id << " " << vec[i][j].d_score << " " << vec[i][j].c_score << endl;
    }

    return 0;
}

1016 部分A+B

思路:

#include <iostream>
using namespace std;

int main()
{
    int a, da, b, db, pa = 0, pb = 0;
    cin >> a >> da >> b >> db;
    while (a)
    {
        if (a % 10 == da) pa = pa * 10 + da;
        a /= 10;
    }
    while (b)
    {
        if (b % 10 == db) pb = pb * 10 + db;
        b /= 10;
    }
    cout << pa + pb;

    return 0;
}

1017 A除以B

思路:

经验总结:

  • 模拟除法的过程写出算法即可。具体的大整数除法可以参考OJ 刷题必备知识总结(二)知识点29、高精度与低精度的除法
  • 注意第二个测试用例:被除数是0时,应该输出 “0 0” 而不是输出 “0”。
  • flag 的作用是,如果高位没有输出过非零数,而被除数 r 小于除数,那么该位的商0就不能输出。
#include <iostream>
using namespace std;

int main()
{
    string a;
    int b, flag = 0, r = 0;         // r 为余数,同时也保存当前的被除数
    cin >> a >> b;
    for (int i = 0; i < a.size(); ++i)
    {
        r = r * 10 + a[i] - '0';    // 余数乘10加到该位上
        if (r >= b)                 // 够除
        {
            cout << r / b;          // 输出该位的商
            r = r % b;              // 计算余数
            flag = 1;               // flag = 1 表明高位输出过非零数
        }
        else                        // 不够除,则该位商为0
             if (flag) cout << "0"; // 高位没输出过非零数,不需要输出0
    }
    if (!flag) cout << "0";         // 商为0需要打印
    cout << " " << r;               // 余数为0的话也需要打印

    return 0;
}

1018 锤子剪刀布

思路:
a,b 分别表示每一次甲乙给出的手势,jia_win[3] 和 yi_win[3] 数组分别统计甲乙某个手势获胜的次数,下标012分别表示 BCJ,maxJ 和 maxY 分别表示甲乙获胜最多的手势对应的下标。由于获胜次数相同时按字典序输出,所以要先比较 B 和 C ,最后再比较 J。

#include <iostream>
using namespace std;

int main()
{
    char a, b, c[4] = {"BCJ"};
    int n, tie = 0, jia_win[3] = {0}, yi_win[3] = {0};
    cin >> n;
    while (n--)
    {
        cin >> a >> b;
        if (a == b) ++tie;                              // 平局计数+1
        else if (a == 'B' && b == 'C') ++jia_win[0];    // 甲用布赢计数+1
        else if (a == 'C' && b == 'J') ++jia_win[1];    // 甲用锤赢计数+1
        else if (a == 'J' && b == 'B') ++jia_win[2];    // 甲用剪赢计数+1
        else if (b == 'B' && a == 'C') ++yi_win[0];     // 乙用布赢计数+1
        else if (b == 'C' && a == 'J') ++yi_win[1];     // 乙用锤赢计数+1
        else if (b == 'J' && a == 'B') ++yi_win[2];     // 乙用剪赢计数+1
    }
    cout << jia_win[0] + jia_win[1] + jia_win[2] << " " << tie << " " << yi_win[0] + yi_win[1] + yi_win[2] << endl;
    cout << yi_win[0] + yi_win[1] + yi_win[2] << " " << tie << " " << jia_win[0] + jia_win[1] + jia_win[2] << endl;
    int maxJ = jia_win[0] >= jia_win[1] ? 0 : 1;		// 寻找甲获胜最多的手势
    maxJ = jia_win[maxJ] >= jia_win[2] ? maxJ : 2;
    int maxY = yi_win[0] >= yi_win[1] ? 0 : 1;			// 寻找乙获胜最多的手势
    maxY = yi_win[maxY] >= yi_win[2] ? maxY : 2;
    cout << c[maxJ] << " " << c[maxY];

    return 0;
}

1019 数字黑洞

思路:
用字符串来直接读取输入,因为 sort 函数,可以很方便的实现重新排列。将其转换为数字求差,再将差转换为字符串,然后按照格式输出即可。

经验总结:

  • 注意循环的结束条件,由于一定要有输出,所以采用 do…while 而不是 while。
  • 题目输入的数、求差后的结果不一定有四位,所以要在字符串前补上字符0。
#include <iostream>
#include <algorithm>
using namespace std;

bool cmp(char a, char b) { return a > b; }

int main()
{
    string s, big, small, result;
    cin >> s;
    s.insert(0, 4 - s.size(), '0'); // s 不够四位时在前面补上字符 '0'
    do
    {
        big = small = s;
        sort(big.begin(), big.end(), cmp);
        sort(small.begin(), small.end());
        int a = stoi(big), b = stoi(small); // stoi() 把字符串转整数
        s = to_string(a - b);               // to_string() 把整数转字符串
        s.insert(0, 4 - s.size(), '0');
        cout << big << " - " << small << " = " << s << endl;
    } while (s != "6174" && s != "0000");

    return 0;
}

1020 月饼

思路:
由描述很容易知道,最大收益策略就是先把单价高的月饼卖掉,所以自然而然地想到要用上 sort 排序函数。有三个维度的数据(第三个数据 “单价” 需要单独计算),又高度绑定在一起,所以最好的办法是定义在一个结构体中。然后在 sort 函数中根据单价的大小关系进行排序。

经验总结:

  • 先计算单价,再将单价乘上销量得到的结果,和放在一个表达式中的结果是一样的,不必担心精度的丢失。
sum += vec[i].unit * d;
// 上述语句等价于下面的
sum += vec[i].price / vec[i].store * d;
  • 可将如下语句用两个等价的三元运算来替换:
if (d >= vec[i].store)
{
    sum += vec[i].price;
    d -= vec[i].store;
}
else
{
    sum += vec[i].unit * d;
    d = 0;
}
// 上面的 if-else 等价于下面的两个三元运算
sum += d >= vec[i].store ? vec[i].price : vec[i].unit * d;
d -= d >= vec[i].store ? vec[i].store : d;

AC 代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
#include <iomanip>
using namespace std;

struct mooncake
{
    double store, price, unit;  // 库存、总售价、单价
};

bool cmp(mooncake a, mooncake b) { return a.unit > b.unit; }

int main()
{
    int n, d;
    cin >> n >> d;
    vector<mooncake> vec(n);
    for (int i = 0; i < n; ++i) cin >> vec[i].store;
    for (int i = 0; i < n; ++i) cin >> vec[i].price;
    for (int i = 0; i < n; ++i)
        vec[i].unit = vec[i].price / vec[i].store;  // 计算每种月饼的单价
    sort(vec.begin(), vec.end(), cmp);              // 按单价从高到低排序
    double sum = 0.0;                               // 最大收益
    for (int i = 0; i < n; ++i)
    {   // 计算当前的价格
        sum += d >= vec[i].store ? vec[i].price : vec[i].unit * d;
		d -= d >= vec[i].store ? vec[i].store : d;
    }
    cout << setiosflags(ios::fixed) << setprecision(2) << sum;

    return 0;
}

1021 个位数统计

#include <iostream>
using namespace std;

int main()
{
    string s;
    cin >> s;
    int digit[10] = {0};
    for (int i = 0; i < s.size(); ++i)
        ++digit[s[i] - '0'];
    for (int i = 0; i < 10; ++i)
        if (digit[i])
            cout << i << ":" << digit[i] << endl;

    return 0;
}

1022 D进制的A+B

思路:

请读者参考OJ 刷题必备知识总结(一)知识点20、进制转换

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int a, b, c, d;
    vector<int> vec;
    cin >> a >> b >> d;
    c = a + b;
    do	// 此处只能用 do...while,不能用 while,详细请参考博客
    {
        vec.push_back(c % d);
        c /= d;
    } while (c);
    for (int i = vec.size() - 1; i >= 0; --i)
        cout << vec[i];

    return 0;
}


希望本篇博客能对你有所帮助,也希望看官能动动小手点个赞哟~~。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值