PAT (Basic Level) Practice 1001~1022

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

文章目录

  • 1001 害死人不偿命的(3n+1)猜想
  • 1002 写出这个数
  • 1003 我要通过!
  • 1004 成绩排名
  • 1005 继续(3n+1)猜想
  • 1006 换个格式输出整数
  • 1007 素数对猜想
  • 1008 数组元素循环右移问题
  • 1009 说反话
  • 1010 一元多项式求导
  • 1011 A+B 和 C
  • 1012 数字分类
  • 1013 数素数
  • 1014 福尔摩斯的约会
  • 1015 德才论
  • 1016 部分A+B
  • 1017 A除以B
  • 1018 锤子剪刀布
  • 1019 数字黑洞
  • 1020 月饼
  • 1021 个位数统计
  • 1022 D进制的A+B


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

思路:

用一个三目运算符即可。

#include 
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位的整数,内置类型 int,long int 和 long long int 都是表示不了的,所以输入的一定是字符串。题目说 n 小于 1 0 100 10^{100} 10100,表明其最多只有一百位,假设每一位都是最大的10,100个10相加都只有1000。所以 n 转换成数字最大也就是999。所以只需要判断三个位的数字即可:

#include 
using namespace std;

int main()
{
    int num = 0;
    char c;
    char s[10][10] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"};
    while ((c = getchar()) != '\n')         // 读取到回车符时结束
        num += c - '0';
    if (num / 100)                          // 判断百位数是否为0
        cout << s[num / 100] << " ";        // num / 100 得到的是百位数
    if (num / 10)                           // 判断十位数是否为0
    cout << s[(num % 100) / 10] << " ";     // (num % 100) / 10 得到的是十位数
    cout << s[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,c 为 AA,b 为 AA,所以 APAAATAAA 也是正确的,以此类推,只要 P 左边是1个 A 且 T 的左右 A 的数量相同都是正确的。
  • 因为 AAPATAA 是正确的,此时 ac 均为 AA,b 为 A,所以 AAPAATAAAA 也是正确的,再推一下 AAPAAATAAAAAA 也是正确的。

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

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

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

#include 
#include 
using namespace std;

int main()
{
    int n, p = 0, t = 0;
    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;     // 用 p 保存 P 出现的位置
            if (s[i] == 'T') t = i;     // 用 t 保存 T 出现的位置
        }
        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 
#include 
using namespace std;

int main()
{
    int n, sc, sc_max = -1, sc_min = 101;
    string name, id, name_max, name_min, id_max, id_min;
    cin >> n;
    while (n--)
    {
        cin >> name >> id >> sc;
        if (sc > sc_max)
        {
            sc_max = sc;
            name_max = name;
            id_max = id;
        }
        if (sc < sc_min)
        {
            sc_min = sc;
            name_min = name;
            id_min = id;
        }
    }
    cout << name_max << " " << id_max << endl;
    cout << name_min << " " << id_min;

    return 0;
}


1005 继续(3n+1)猜想

思路:

题目的意思就是,给出一行正整数,没有出现在递推过程中的数就称为关键数字,程序要能按从大到小的顺序输出这一行正整数中的关键数字。可以先根据输入的整数建立一个 map,左值是数,右值为初始化为1表示未出现过(未被覆盖),为0表示出现过(被覆盖)。然后遍历 map,对所有整数进行一次 3n + 1 递推,这个过程中出现过的所有数字,将其存入 map 并将值置为1。最后打印所有值为1的键即可。

  • 由于 map 容器会自动从小到大排序,所以打印时从尾元素开始往首元素检查值。
  • 注意空格的处理。
#include 
#include 
using namespace std;

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

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

    return 0;
}


1007 素数对猜想

思路:

对于素数算法不了解的读者可以阅读这篇文章:OJ 刷题必备知识总结(二)知识点26。

#include 
#include 
using namespace std;

int main()
{
    int n, count = 0;
    cin >> n;
    vector<int> prime;                      // prime 存储所有的素数
    for (int i = 2; i <= n; ++i)            // 枚举2 ~ n 的所有数
    {
        int flag = 1;                       // flag 为1表明 i 是素数
        for (int j = 2; j * j <= i; ++j)    // 素数判定
            if (i % j == 0)                 // 存在除1和本身外的因子
                flag = 0;                   // flag 置0表明其是合数
        if (flag) prime.push_back(i);       // flag 仍为1,将 i 加入素数表
    }

    for (int i = 1; i < prime.size(); ++i)  // 统计符合题目要求的素数对
        if (prime[i] - prime[i - 1] == 2)
            ++count;
    cout << count;

    return 0;
}


1008 数组元素循环右移问题

思路:

读者可以手写题给数组平移前后的数组下标对比,就可以发现其中的规律。假设初始下标应为 a,平移后的为 b,则有:
b = ( a + m ) % n b = (a + m) \% n b=(a+m)%n
取余是因为 m 有可能大于 n(第三个测试数据)。

#include 
#include 
using namespace std;

int main()
{
    int n, m, k;
    cin >> n >> m;
    vector<int> vec1(n, 0);         // 定义大小为 n 的数组,初始值全0
    for (int i = 0; i < n; ++i)		
    {
        cin >> k;
        vec1[(i + m)% n] = k;		// 输入时就移动整数到其最终位置
    }

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

    return 0;
}


1009 说反话

思路:

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

#include
#include
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 
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 
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 
#include 
using namespace std;

int main()
{
    vector<int> vec[5];     // 定义一个二维数组
    int n, k, sum1 = 0, sum2 = 0, sum3 = 0, max = 0;
    scanf("%d", &n);
    while (n--)             // 循环输入 n 个数
    {
        scanf("%d", &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 && j % 2 == 0) sum2 += vec[i][j];            // j 从0开始的,所以要注意判定条件
            if (i == 1 && j % 2 == 1) sum2 += -vec[i][j];
            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) printf(" ");        // 只有 A1 不输出前面的空格
        if (i == 0 && sum1 == 0 || i != 0 && vec[i].size() == 0) printf("N");   // 分类中不存在数字,输出 N
        else
        {
            if (i == 0) printf("%d", sum1);
            if (i == 1) printf("%d", sum2);
            if (i == 2) printf("%d", vec[2].size());
            if (i == 3) printf("%.1f", (sum3 * 1.0) / vec[3].size());           // 求平均数,记得乘上1.0转为浮点数
            if (i == 4) printf("%d", 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 
using namespace std;

int main()
{
    int m, n, count = 0;
    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;
        if (flag) ++count;                  // i 是素数,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,这样子是可以的。但是最好不要 cout 和 printf 混用,cin 和 scanf 混用。
#include 
#include 
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;                                        // 递增一下 i,要不然在下面的循环中又被输出了
    for (; i < s1.size() && i < s2.size(); ++i) // 寻找前面两个字符串中第二对相同的大写字母或数字字符
    {
        if (s1[i] == s2[i] && (isdigit(s1[i]) || 'A' <= s1[i] && s1[i] <= 'N'))
        {   // 数字字符与大写字母的输出不一样,注意输出格式,小时和分钟都要占用两位
            isdigit(s1[i]) ? printf("%02d:", s1[i] - '0') : printf("%02d:", s1[i] - 'A' + 10);
            break;                              // 退出循环
        }
    }
    for (i = 0; i < s3.size() && i < s4.size(); ++i)
    {
        if (s3[i] == s4[i] && isalpha(s3[i]))   // 寻找后面两个字符串中第一对相同的字母
        {
            printf("%02d", i);
            break;                              // 退出循环
        }
    }

    return 0;
}


1015 德才论

思路:

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

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

经验总结:

  • 对二维数组的定义采用了 1012 数字分类 中一样的做法。
  • 先判断有没有过线,再进行考生分类,可以缩减语句,否则每个判断中都需要花时间判断是否过线。
  • scanf 在某些情况下确实比 cin 好用,比如在读取考生数据的上,对比如下:

如果使用 cin 的话还需要在 stu 结构体中写构造函数才能初始化相关变量:

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;
    }
};
...
cin >> i >> d >> c;
struct stu temp(i, d, c);

用 scanf 就可以直接一条语句完成初始化。:

struct stu
{
    int id, d_score, c_score, t_score;
};
...
struct stu temp;
scanf("%d%d%d", &temp.id, &temp.d_score, &temp.c_score);

最后的 AC 代码如下:

#include 
#include 
#include 
using namespace std;

struct stu
{	// 考生结构体,包含准考证号、德分和才分成员变量
    int id, d, c;
};

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

int main()
{
    int m, l, h;
    vector<stu> vec[4];             // 定义一个 vec 数组,每个元素都是一个 vector
    scanf("%d%d%d", &m, &l, &h);    // 输入考生人数、录取最低分和优先录取线
    while (m--)
    {
        struct stu temp;            // temp 存储考生的准考证号、德分和才分
        scanf("%d%d%d", &temp.id, &temp.d, &temp.c);
        if (temp.d >= l && temp.c >= l)
        {   // 筛选掉德才分均未过最低录取线的考生
            if (temp.d >=h && temp.c >= h) vec[0].push_back(temp);                          // 第一类考生
            else if (temp.d >= h && temp.c < h) vec[1].push_back(temp);                     // 第二类考生
            else if (temp.d < h && temp.c < h && temp.d >= temp.c) vec[2].push_back(temp);  // 第三类考生
            else if (temp.d < h) vec[3].push_back(temp);                                    // 第四类考生
        }
    }

    printf("%d\n", vec[0].size() + vec[1].size() + vec[2].size() + vec[3].size());          // 一行打印考生人数
    for (int i = 0; i < 4; ++i)
    {   // 对每一类考生进行排序并进行输出
        sort(vec[i].begin(), vec[i].end(), compare);
        for(int j = 0; j < vec[i].size(); ++j)
            printf("%d %d %d\n", vec[i][j].id, vec[i][j].d, vec[i][j].c);
    }

    return 0;
}


1016 部分A+B

思路:

1 0 9 10^9 109 大小的数据需要用 long long int 来定义,另外 P A P_A PA P B P_B PB也需要,因为有可能 A 和 B 的所有位数都相同。

#include 
using namespace std;

int main()
{
    long long 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 
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 
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 
#include 
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');         // 用0补齐头部的空缺字符
    do
    {
        big = small = s;
        sort(big.begin(), big.end(), cmp);  // 从大到小排序得到递减序列
        sort(small.begin(), small.end());   // 从小到大排序得到递增序列
        int a = stoi(big), b = stoi(small); // 将递减序列和递增序列转换为对应的整数
        s = to_string(a - b);               // 将它们的差转回字符串保存在 s 中
        s.insert(0, 4 - s.size(), '0');     // 用0补齐头部的空缺字符
        cout << big << " - " << small << " = " << s << endl;
    } while (s != "6174" && s != "0000");   // s 为数字黑洞或0时退出循环

    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 ? sum + vec[i].price : sum + vec[i].unit * d;
d = d >= vec[i].store ? d - vec[i].store : 0;

AC 代码如下:

#include 
#include 
#include 
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) scanf("%lf", &vec[i].store);
    for (int i = 0; i < n; ++i) scanf("%lf", &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 ? sum + vec[i].price : sum + vec[i].unit * d;
        d = d >= vec[i].store ? d - vec[i].store : 0;
    }
    printf("%.2f", sum);

    return 0;
}


1021 个位数统计

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


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

你可能感兴趣的:(PTA,c++,算法,PAT,Basic,Level,OJ,数据结构)