PTA寒假基础题训练(含解题思路)(下)

目录

7-36 字符串的冒泡排序

AC代码:

7-37 龟兔赛跑

AC代码:

7-38 N个数求和

AC代码:

7-39 数列求和-加强版

AC代码:

7-40 抓老鼠啊~亏了还是赚了?

AC代码:

7-41 整除光棍

AC代码:

7-42 谷歌的招聘

AC代码:

7-43 古风排版

AC代码:

7-44 阅览室

AC代码:

7-45 天梯赛座位分配

AC代码:

7-46 天梯赛的善良

AC代码:

7-47 敲笨钟

AC代码:

7-48 IP地址转换

AC代码:

7-49 估值一亿的AI核心代码

AC代码:

7-50 最长对称子串(动态规划)

AC代码:

7-51 月饼(贪心)

AC代码:

7-52 彩虹瓶(数据结构:栈)

AC代码:


因题集题目较多,另外两部分题集请移步这里:

PTA寒假基础题训练(含解题思路)(上)_清晨喝碗粥的博客-CSDN博客

PTA寒假基础题训练(含解题思路)(中)_清晨喝碗粥的博客-CSDN博客

7-36 字符串的冒泡排序

我们已经知道了将N个整数按从小到大排序的冒泡排序法。本题要求将此方法用于字符串序列,并对任意给定的K(<N),输出扫描完第K遍后的中间结果序列。

输入格式:

输入在第1行中给出N和K(1≤K<N≤100),此后N行,每行包含一个长度不超过10的、仅由小写英文字母组成的非空字符串。

输出格式:

输出冒泡排序法扫描完第K遍后的中间结果序列,每行包含一个字符串。

输入样例:

6 2
best
cat
east
a
free
day

输出样例:

best
a
cat
day
east
free

思路:不清楚冒泡排序的话可以去了解冒泡排序的实现逻辑,此题为单纯考察冒泡排序,实现代码如下 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, m;
    cin >> n >> m;
    vector<string>nums(n, "");
    for (i = 0; i < n; i++) {
        cin >> nums[i];
    }
    for (i = 0; i < m; i++) {
        for (j = 1; j < n; j++) {
            if (nums[j] < nums[j - 1])
                swap(nums[j], nums[j - 1]);
        }
    }
    for (i = 0; i < nums.size(); i++) {
        cout << nums[i] << endl;
    }


    system("pause");
    return 0;
}

7-37 龟兔赛跑

乌龟与兔子进行赛跑,跑场是一个矩型跑道,跑道边可以随地进行休息。乌龟每分钟可以前进3米,兔子每分钟前进9米;兔子嫌乌龟跑得慢,觉得肯定能跑赢乌龟,于是,每跑10分钟回头看一下乌龟,若发现自己超过乌龟,就在路边休息,每次休息30分钟,否则继续跑10分钟;而乌龟非常努力,一直跑,不休息。假定乌龟与兔子在同一起点同一时刻开始起跑,请问T分钟后乌龟和兔子谁跑得快?

输入格式:

输入在一行中给出比赛时间T(分钟)。

输出格式:

在一行中输出比赛的结果:乌龟赢输出@_@,兔子赢输出^_^,平局则输出-_-;后跟1空格,再输出胜利者跑完的距离。

输入样例:

242

输出样例:

@_@ 726

思路:此题在逻辑上可能会比较绕,但逻辑捋清楚了题目基本不难,具体实现逻辑可以参见代码 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, t, sum1 = 0, sum2 = 0, time1 = 10, time2 = 0;
    cin >> t;
    for (i = 0; i < t; i++) {
        sum1 += 3;
        if (time2 == 0) {
            sum2 += 9; 
            time1--;
        } else
            time2--;
        if (time1 == 0) {
            if (sum2 > sum1)
                time2 = 30;
            time1 = 10;
        }
    }
    if (sum1 > sum2)
        cout << "@_@" << " " << sum1 << endl;
    else if (sum1 < sum2)
        cout << "^_^" << " " << sum2 << endl;
    else
        cout << "-_-" << " " << sum1 << endl;


    system("pause");
    return 0;
}

7-38 N个数求和

本题的要求很简单,就是求N个数字的和。麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和也必须是有理数的形式。

输入格式:

输入第一行给出一个正整数N(≤100)。随后一行按格式a1/b1 a2/b2 ...给出N个有理数。题目保证所有分子和分母都在长整型范围内。另外,负数的符号一定出现在分子前面。

输出格式:

输出上述数字和的最简形式 —— 即将结果写成整数部分 分数部分,其中分数部分写成分子/分母,要求分子小于分母,且它们没有公因子。如果结果的整数部分为0,则只输出分数部分。

输入样例1:

5
2/5 4/15 1/30 -2/60 8/3

输出样例1:

3 1/3

输入样例2:

2
4/3 2/3

输出样例2:

2

输入样例3:

3
1/3 -1/6 1/8

输出样例3:

7/24

思路:对每对分数进行求和,先求分母的最小公倍数(目的:保持分母一致),分子再分别乘以另外一个分数的分母,随后分子相加即可,全部累加完然后对分数进行约分,因为在计算的过程中可能会越界int类型的范围,所以开long long类型比较好

这里提示一下:因为C++ algorithm算法库只提供了求最大公约数的函数,所以最小公倍数可以利用二者的成绩除以二者的最大公约数去求

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long i, j, k, n, mole = 0, deno = 1;
    cin >> n;
    vector<string>nums(n, "");
    for (i = 0; i < n; i++) {
        cin >> nums[i];
    }
    for (i = 0; i < n; i++) {
        long long item_mole = stol(nums[i].substr(0, nums[i].find('/')));
        long long item_deno = stol(nums[i].substr(nums[i].find('/') + 1, nums[i].length() - nums[i].find('/') - 1));
        long long lcm = deno * item_deno / __gcd(deno, item_deno);
        mole *= lcm / deno;
        item_mole *= lcm / item_deno;
        mole = mole + item_mole;
        deno = lcm;
    }
    long long gcd = __gcd(mole, deno);
    mole /= gcd;
    deno /= gcd;
    if (deno == 1)
        cout << mole << endl;
    else {
        if (mole / deno * deno == mole) {
            cout << mole / deno << endl;
        } else if (mole > deno) {
            int a = mole / deno;
            mole -= a * deno;
            cout << a << " " << mole << "/" << deno << endl;
        }else {
            cout << mole << "/" << deno << endl;
        }
    }

    system("pause");
    return 0;
}

7-39 数列求和-加强版

给定某数字A(1≤A≤9)以及非负整数N(0≤N≤100000),求数列之和S=A+AA+AAA+⋯+AA⋯A(N个A)。例如A=1, N=3时,S=1+11+111=123。

输入格式:

输入数字A与非负整数N。

输出格式:

输出其N项数列之和S的值。

输入样例:

1 3

输出样例:

123

思路:拿S = 1 + 11 + 111举例,其实对应的就是每一位相加,即

1      +

11     +

111    =

321

开一个数组对每一位逐位计算保存即可

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long i, j, k, n, a, item = 0;
    string s;
    cin >> a >> n;
    vector<int>nums(n + 1, 0);
    if (n == 0) {
        cout << 0 << endl;
        return 0;
    }
    for (i = n; i >= 1; i--) {
        nums[i] = (i * a + item) % 10;
        item = (i * a + item) / 10;
    }
    if (item != 0)
        cout << item;
    for (i = 1; i <= n; i++) {
        cout << nums[i];
    }

    system("pause");
    return 0;
}

7-40 抓老鼠啊~亏了还是赚了?

某地老鼠成灾,现悬赏抓老鼠,每抓到一只奖励10元,于是开始跟老鼠斗智斗勇:每天在墙角可选择以下三个操作:放置一个带有一块奶酪的捕鼠夹(T),或者放置一块奶酪(C),或者什么也不放(X)。捕鼠夹可重复利用,不计成本,奶酪每块3元。

聪明的老鼠呢?它们每天可能会派出一只老鼠到墙角,看看墙角有啥:

  • 若什么也没有(X),老鼠们就不高兴了(Unhappy),会有长达一天(也就是第二天)的不高兴期。在不高兴期间,不派出老鼠。不高兴期结束之后,派出老鼠。
  • 若有捕鼠夹(T),这只老鼠被引诱吃掉奶酪并被打死(Dead),老鼠们会有长达两天(也就是第二和第三天)的伤心期。在伤心期间,不派出老鼠。伤心期结束之后,派出老鼠。在这种情况下,抓到1只老鼠可获得奖励10元,但同时也耗费了一块奶酪。注意,如果某一天放置了捕鼠夹但老鼠没有出现,则没有耗费奶酪。
  • 若有奶酪(C),老鼠吃了奶酪会很开心(Happy!),会有长达两天(第二和第三天)的兴奋期。在兴奋期间,即使叠加了不高兴或者伤心,也必定派出老鼠。在这种情况下,没抓到老鼠,而且耗费了一块奶酪。注意,如果某一天放置了奶酪但老鼠没有出现,则奶酪可以下次再用,没有耗费。

现在给你连续几天的操作序列,且已知第一天肯定会派出老鼠,请判断老鼠每天的状态,并计算盈利。

输入格式:

输入在一行中给出连续的由CTX组成的不超过70个字符的字符串,以$结束。字符串中每个字符表示这一天的操作( 即X:什么都不放;T:放捕鼠夹;C:放奶酪)。题目保证至少有一天的操作输入。

输出格式:

要求在第一行输出连续的字符串,与输入相对应,给出老鼠的状态:

  • ! 表示派出老鼠吃到奶酪
  • D 表示派出老鼠被打死
  • U 表示派出老鼠无所获
  • - 表示没有派出老鼠

第二行则应输出一个整数表示盈利。(如果有亏损,则是负数)

输入样例1:

TXXXXC$

输出样例1:

D--U-! 
4

输入样例2:

CTTCCX$

输出样例2:

!DD--U 
11

思路:此题没什么其他难点,主要难点便需要处理好逻辑,逻辑捋清楚了即简单题,具体实现如下代码 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, count = 0, hap_day = 0, sad_day = 0;
    string s, res;
    char op;
    while ((op = getchar()) != '$') {
        s += op;
    }
    for (i = 0; i < s.length(); i++) {
        if (sad_day && !hap_day) {
            sad_day--;
            res += "-";
        } else {
            if (hap_day)
                hap_day--;
            if (s[i] == 'T') {
                res += "D";
                sad_day = 2;
                count += 7;
            } else if (s[i] == 'X') {
                res += "U";
                sad_day = 1;
            } else if (s[i] == 'C') {
                res += "!";
                hap_day = 2;
                count -= 3;
            }
        }
    }
    cout << res << endl;
    cout << count << endl;
    
    system("pause");
    return 0;
}

7-41 整除光棍

这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。

提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。

输入格式:

输入在一行中给出一个不以5结尾的正奇数x(<1000)。

输出格式:

在一行中输出相应的最小的sn,其间以1个空格分隔。

输入样例:

31

输出样例:

3584229390681 15

思路:模拟竖式除法,我们可以先找到刚好大于数字n的光棍数字,然后依次保存商,其次对每次相除的余数进行判断,若余数不为0,则令被除数 = 余数 * 10 + 1(即向后补1),直到余数为0为止 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, n, x = 1, count = 1;
    cin >> n;
    vector<int>nums;
    while (x < n) {
        x = x * 10 + 1;
        count++;
    }
    while (1) {
        int item = x % n;
        nums.push_back(x / n);
        if (item == 0) break;
        x = item * 10 + 1;
        count++;
    }
    for (i = 0; i < nums.size(); i++) {
        cout << nums[i];
    }
    cout << " " << count << endl;
    
    system("pause");
    return 0;
}

7-42 谷歌的招聘

2004 年 7 月,谷歌在硅谷的 101 号公路边竖立了一块巨大的广告牌(如下图)用于招聘。内容超级简单,就是一个以 .com 结尾的网址,而前面的网址是一个 10 位素数,这个素数是自然常数 e 中最早出现的 10 位连续数字。能找出这个素数的人,就可以通过访问谷歌的这个网站进入招聘流程的下一步。

自然常数 e 是一个著名的超越数,前面若干位写出来是这样的:e = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921... 其中粗体标出的 10 位数就是答案。

本题要求你编程解决一个更通用的问题:从任一给定的长度为 L 的数字中,找出最早出现的 K 位连续数字所组成的素数。

输入格式:

输入在第一行给出 2 个正整数,分别是 L(不超过 1000 的正整数,为数字长度)和 K(小于 10 的正整数)。接下来一行给出一个长度为 L 的正整数 N。

输出格式:

在一行中输出 N 中最早出现的 K 位连续数字所组成的素数。如果这样的素数不存在,则输出 404。注意,原始数字中的前导零也计算在位数之内。例如在 200236 中找 4 位素数,0023 算是解;但第一位 2 不能被当成 0002 输出,因为在原始数字中不存在这个 2 的前导零。

输入样例1:

20 5
23654987725541023819

输出样例1:

49877

输入样例2:

10 3
2468001680

输出样例2:

404

思路:利用C++取子串API接口取子串,然后依次进行素数判断即可 

AC代码:

#include<bits/stdc++.h>
using namespace std;
bool Is_prime(int n) {
    if (n == 0 || n == 1) return false;
    if (n == 2) return true;
    int item = sqrt(n) + 1;
    for (int i = 2; i <= item; i++) {
        if (n % i == 0) return false;
    }
    return true;
}
int main()
{
    int i, j, k, n;
    string s;
    cin >> n >> k;
    cin >> s;
    for (i = 0; i <= n - k; i++) {
        string x = s.substr(i, k);
        int item = stoi(x);
        if (Is_prime(item)) {
            cout << x << endl;
            break;
        }
    }
    if (i > n - k) {
        cout << 404 << endl;
    }
    
    system("pause");
    return 0;
}

7-43 古风排版

中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。

输入格式:

输入在第一行给出一个正整数N(<100),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。

输出格式:

按古风格式排版给定的字符串,每列N个字符(除了最后一列可能不足N个)。

输入样例:

4
This is a test case

输出样例:

asa T
st ih
e tsi
 ce s

思路:依据题意模拟即可,最后一列若不够则用空格代替 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n;
    cin >> n;
    getchar();
    char * ch = new char[1005];
    cin.getline(ch, 1005, '\n');
    string s = ch;
    vector<string>nums(n);
    for (i = 0, j = 0; i < s.length(); i++, j = j % n) {
        nums[j] = s[i] + nums[j];
        j++;
    }
    for (i = 0; i < n; i++) {
        if (nums[i].length() < nums[0].length())
            nums[i] = ' ' + nums[i];
    }
    for (i = 0; i < n; i++) {
        cout << nums[i] << endl;
    }
    
    system("pause");
    return 0;
}

7-44 阅览室

天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时,管理员输入书号并按下S键,程序开始计时;当读者还书时,管理员输入书号并按下E键,程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时,表示一天工作结束,你的程序应输出当天的读者借书次数和平均阅读时间。

注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有S没有E,或者只有E没有S的纪录,系统应能自动忽略这种无效纪录。另外,题目保证书号是书的唯一标识,同一本书在任何时间区间内只可能被一位读者借阅。

输入格式:

输入在第一行给出一个正整数N(≤10),随后给出N天的纪录。每天的纪录由若干次借阅操作组成,每次操作占一行,格式为:

书号([1, 1000]内的整数) 键值SE) 发生时间hh:mm,其中hh是[0,23]内的整数,mm是[0, 59]内整数)

每一天的纪录保证按时间递增的顺序给出。

输出格式:

对每天的纪录,在一行中输出当天的读者借书次数和平均阅读时间(以分钟为单位的精确到个位的整数时间)。

输入样例:

3
1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00

输出样例:

2 196
0 0
1 60

思路:这题有一些坑点,比如如果有多个借书记录,以最后一次为准,如果有多个还书记录,以第一次为准,平均阅读时间是要四舍五入的,这里我采用哈希表的形式,如果有借书记录且哈希表内未存储,则将其存储进去,如果哈希表内存储过则覆盖时间,如果还书的时候先判断哈希表内是否有借书记录,有则将时间清零(赋值为-1)否则进入下一条记录的判断 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, time = 0, count = 0;
    cin >> n;
    //key为书编号value为时间(均转化为分钟)
    map<int, int>mp;
    while (n) {
        int num, hh, ss;
        char op, key;
        scanf("%d %c %d %c %d", &num, &key, &hh, &op, &ss);
        if (key == 'S') {
            mp[num] = hh * 60 + ss;
        } else if (key == 'E') {
            if (mp.find(num) != mp.end() && mp[num] != -1) {
                time += hh * 60 + ss - mp[num];
                mp[num] = -1;
                count++;
            }
        }
        if (num == 0) {
            if (count != 0)
                printf("%d %.0lf\n", count, time * 1.0 / count);
            else 
                cout << 0 << " " << 0 << endl;
            mp.clear();
            count = 0;
            time = 0;
            n--;
        }
    }
    
    system("pause");
    return 0;
}

7-45 天梯赛座位分配

天梯赛每年有大量参赛队员,要保证同一所学校的所有队员都不能相邻,分配座位就成为一件比较麻烦的事情。为此我们制定如下策略:假设某赛场有 N 所学校参赛,第 i 所学校有 M[i] 支队伍,每队 10 位参赛选手。令每校选手排成一列纵队,第 i+1 队的选手排在第 i 队选手之后。从第 1 所学校开始,各校的第 1 位队员顺次入座,然后是各校的第 2 位队员…… 以此类推。如果最后只剩下 1 所学校的队伍还没有分配座位,则需要安排他们的队员隔位就坐。本题就要求你编写程序,自动为各校生成队员的座位号,从 1 开始编号。

输入格式:

输入在一行中给出参赛的高校数 N (不超过100的正整数);第二行给出 N 个不超过10的正整数,其中第 i 个数对应第 i 所高校的参赛队伍数,数字间以空格分隔。

输出格式:

从第 1 所高校的第 1 支队伍开始,顺次输出队员的座位号。每队占一行,座位号间以 1 个空格分隔,行首尾不得有多余空格。另外,每所高校的第一行按“#X”输出该校的编号X,从 1 开始。

输入样例:

3
3 4 2

输出样例:

#1
1 4 7 10 13 16 19 22 25 28
31 34 37 40 43 46 49 52 55 58
61 63 65 67 69 71 73 75 77 79
#2
2 5 8 11 14 17 20 23 26 29
32 35 38 41 44 47 50 53 56 59
62 64 66 68 70 72 74 76 78 80
82 84 86 88 90 92 94 96 98 100
#3
3 6 9 12 15 18 21 24 27 30
33 36 39 42 45 48 51 54 57 60

思路:分情况讨论,若队伍数 < n – 1,则隔位就做,否则交叉队伍就做 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n;
    cin >> n;
    vector<int>nums(n, 0);
    vector<vector<int>>res(n);
    set<int>st;
    for (i = 0; i < n; i++) {
        cin >> nums[i];
        res[i].resize(nums[i] * 10);
    }
    j = 0;
    k = 1;
    while (st.size() != n) {
        if (st.size() < n - 1) {
            for (i = 0; i < n; i++) {
                if (j < res[i].size()) {
                    res[i][j] = k;
                    k++;
                } 
                if (j == res[i].size() - 1) {
                    st.insert(i);
                }
            }
        } else {
            for (i = 0; i < n; i++) {
                if (st.count(i) == 0) {
                    if (st.size() != 0)
                        k = max(k, res[i][j - 1] + 2);
                    for (j; j < res[i].size(); j++) {
                        res[i][j] = k;
                        k += 2;
                    }
                }
                st.insert(i);
            }
        }
        j++;
    }
    for (i = 0; i < n; i++) {
        cout << "#" << i + 1 << endl;
        for (j = 0; j < res[i].size(); j++) {
            if ((j + 1) % 10 == 1)
                cout << res[i][j];
            else 
                cout << " " << res[i][j];
            if ((j + 1) % 10 == 0)
                cout << endl;
        }
    }

    
    system("pause");
    return 0;
}

7-46 天梯赛的善良

天梯赛是个善良的比赛。善良的命题组希望将题目难度控制在一个范围内,使得每个参赛的学生都有能做出来的题目,并且最厉害的学生也要非常努力才有可能得到高分。

于是命题组首先将编程能力划分成了 106 个等级(太疯狂了,这是假的),然后调查了每个参赛学生的编程能力。现在请你写个程序找出所有参赛学生的最小和最大能力值,给命题组作为出题的参考。

输入格式:

输入在第一行中给出一个正整数 N(≤2×10^4),即参赛学生的总数。随后一行给出 N 个不超过 10^6 的正整数,是参赛学生的能力值。

输出格式:

第一行输出所有参赛学生的最小能力值,以及具有这个能力值的学生人数。第二行输出所有参赛学生的最大能力值,以及具有这个能力值的学生人数。同行数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

10
86 75 233 888 666 75 886 888 75 666

输出样例:

75 3
888 2

思路:简单模拟题,利用哈希表+排序或者标记数组+排序依据题意模拟即可 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n;
    cin >> n;
    vector<int>nums(n, 0);
    map<int, int>mp;
    for (i = 0; i < n; i++) {
        cin >> nums[i];
        mp[nums[i]]++;
    }
    sort(nums.begin(), nums.end());
    cout << nums[0] << " " << mp[nums[0]] << endl;
    cout << nums[nums.size() - 1] << " " << mp[nums[nums.size() - 1]] << endl;

    
    system("pause");
    return 0;
}

7-47 敲笨钟

微博上有个自称“大笨钟V”的家伙,每天敲钟催促码农们爱惜身体早点睡觉。为了增加敲钟的趣味性,还会糟改几句古诗词。其糟改的方法为:去网上搜寻压“ong”韵的古诗词,把句尾的三个字换成“敲笨钟”。例如唐代诗人李贺有名句曰:“寻章摘句老雕虫,晓月当帘挂玉弓”,其中“虫”(chong)和“弓”(gong)都压了“ong”韵。于是这句诗就被糟改为“寻章摘句老雕虫,晓月当帘敲笨钟”。

现在给你一大堆古诗词句,要求你写个程序自动将压“ong”韵的句子糟改成“敲笨钟”。

输入格式:

输入首先在第一行给出一个不超过 20 的正整数 N。随后 N 行,每行用汉语拼音给出一句古诗词,分上下两半句,用逗号 , 分隔,句号 . 结尾。相邻两字的拼音之间用一个空格分隔。题目保证每个字的拼音不超过 6 个字符,每行字符的总长度不超过 100,并且下半句诗至少有 3 个字。

输出格式:

对每一行诗句,判断其是否压“ong”韵。即上下两句末尾的字都是“ong”结尾。如果是压此韵的,就按题面方法糟改之后输出,输出格式同输入;否则输出 Skipped,即跳过此句。

输入样例:

5
xun zhang zhai ju lao diao chong, xiao yue dang lian gua yu gong.
tian sheng wo cai bi you yong, qian jin san jin huan fu lai.
xue zhui rou zhi leng wei rong, an xiao chen jing shu wei long.
zuo ye xing chen zuo ye feng, hua lou xi pan gui tang dong.
ren xian gui hua luo, ye jing chun shan kong.

输出样例:

xun zhang zhai ju lao diao chong, xiao yue dang lian qiao ben zhong.
Skipped
xue zhui rou zhi leng wei rong, an xiao chen jing qiao ben zhong.
Skipped
Skipped

思路:用一个string变量事先存储 qiao ben zhong.,然后利用string类型的取子串操作对ong进行对比,若符合压ong韵,则将末尾的ong替换为qiao ben zhong. 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n;
    string item = " qiao ben zhong.";
    cin >> n;
    getchar();
    vector<string>nums(n, "");
    for (i = 0; i < n; i++) {
        char * ch = new char [105];
        cin.getline(ch, 105, '\n');
        nums[i] = ch;
    }
    for (i = 0; i < n; i++) {
        if (nums[i].find(',') >= 3 && nums[i].substr(nums[i].find(',') - 3, 3) == nums[i].substr(nums[i].length() - 4, 3) && nums[i].substr(nums[i].length() - 4, 3) == "ong") {
            string s;
            int count = 0;
            for (j = nums[i].length() - 1; count < 3; j--) {
                if (nums[i][j] == ' ')
                    count++;
            }
            s = nums[i].substr(0, j + 1) + item;
            nums[i] = s;
        } else {
            nums[i] = "Skipped";
        }
    }
    for (i = 0; i < n; i++) {
        cout << nums[i] << endl;
    }
    
    system("pause");
    return 0;
}

7-48 IP地址转换

一个IP地址是用四个字节(每个字节8个位)的二进制码组成。请将32位二进制码表示的IP地址转换为十进制格式表示的IP地址输出。

输入格式:

输入在一行中给出32位二进制字符串。

输出格式:

在一行中输出十进制格式的IP地址,其由4个十进制数组成(分别对应4个8位的二进制数),中间用“.”分隔开。

输入样例:

11001100100101000001010101110010

输出样例:

204.148.21.114

思路:把32为二进制字符串平均分成4组8位二进制数,依次对其进行求十进制即可 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int binary (string s) {
    int i, k, sum = 0;
    for (i = 0, k = 7; i < s.length(); i++, k--) {
        if (s[i] == '1') {
            sum += pow(2, k);
        }
    }
    return sum;
}
int main()
{
    int i, j, k, n;
    string s;
    cin >> s;
    vector<int>nums;
    for (i = 0; i < s.length(); i += 8) {
        string item = s.substr(i, 8);
        nums.push_back(binary(item));
    }
    for (i = 0; i < nums.size(); i++) {
        if (i == 0)
            cout << nums[i];
        else
            cout << "." << nums[i];
    }
    
    system("pause");
    return 0;
}

7-49 估值一亿的AI核心代码

 以上图片来自新浪微博。

本题要求你实现一个稍微更值钱一点的 AI 英文问答程序,规则是:

  • 无论用户说什么,首先把对方说的话在一行中原样打印出来;
  • 消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
  • 把原文中所有大写英文字母变成小写,除了 I
  • 把原文中所有独立的 can youcould you 对应地换成 I canI could—— 这里“独立”是指被空格或标点符号分隔开的单词;
  • 把原文中所有独立的 I 和 me 换成 you
  • 把原文中所有的问号 ? 换成惊叹号 !
  • 在一行中输出替换后的句子作为 AI 的回答。

输入格式:

输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。

输出格式:

按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。

输入样例:

6
Hello ?
 Good to chat   with you
can   you speak Chinese?
Really?
Could you show me 5
What Is this prime? I,don 't know

输出样例:

Hello ?
AI: hello!
 Good to chat   with you
AI: good to chat with you
can   you speak Chinese?
AI: I can speak chinese!
Really?
AI: really!
Could you show me 5
AI: I could show you 5
What Is this prime? I,don 't know
AI: what Is this prime! you,don't know

思路:此题用C写会比较麻烦,C++内置了清除erase、替换replace等API接口,写起来想对容易些,细节方面可以参见以下代码,代码块我已写好注释,这里提醒一点,所有操作均在原文中处理 

AC代码:

#include<bits/stdc++.h>
using namespace std;
char transform (char op) {
    if (op == 'I')
        return op;
    if (op >= 'A' && op <= 'Z')
        return op + 32;
    return op;
}
int main()
{
    int i, j, k, n;
    cin >> n;
    getchar();
    vector<string>nums;
    while (n--) {
        char * ch = new char [1005];
        cin.getline(ch, 1005, '\n');
        nums.push_back(ch);
    }
    for (i = 0; i < nums.size(); i++) {
        cout << nums[i] << endl;
        //去除开头的空格
        while (nums[i][0] == ' ') {
            nums[i].erase(nums[i].begin());
        }
        //去除结尾的空格
        while (nums[i][nums[i].length() - 1] == ' ') {
            nums[i].erase(nums[i].end() -1);
        }
        for (j = 0; j < nums[i].length(); j++) {
            //对单词之间的空格进行去重
            if (nums[i][j] == ' ') {
                while (nums[i][j + 1] == ' ') {
                    nums[i].erase(nums[i].begin() + j + 1);
                }
                //不是标点插入空格
                if (nums[i][j + 1] < '0' || nums[i][j + 1]>'9' && nums[i][j + 1] < 'A' || nums[i][j + 1] > 'Z' && nums[i][j + 1] < 'a' || nums[i][j + 1] > 'z') {
                    nums[i].erase(nums[i].begin() + j);
                }
            }
        }
        //大小写转换
        for (j = 0; j < nums[i].length(); j++) {
            nums[i][j] = transform(nums[i][j]);
        }
    
        int index;
        // 独立单词转换
        for (index = nums[i].find("can you"); index < nums[i].length() && index != -1; index = nums[i].find("can you", index)) {
            if ((index == 0 || nums[i][index - 1] < '0' || nums[i][index - 1] > '9' && nums[i][index - 1] < 'A' || nums[i][index - 1] > 'Z' && nums[i][index - 1] < 'a' || nums[i][index - 1] > 'z') 
            && (index + 7 == nums[i].length() || nums[i][index + 7] < '0' || nums[i][index + 7] > '9' && nums[i][index + 7] < 'A' || nums[i][index + 7] > 'Z' && nums[i][index + 7] < 'a' || nums[i][index + 7] > 'z')) {
                nums[i].replace(index, 7, "* can");
            }
            index++;
        }
        for (index = nums[i].find("could you"); index < nums[i].length() && index != -1; index = nums[i].find("could you", index)) {
            if ((index == 0 || nums[i][index - 1] < '0' || nums[i][index - 1] > '9' && nums[i][index - 1]<'A' || nums[i][index - 1] > 'Z' && nums[i][index - 1] < 'a' || nums[i][index - 1]>'z') 
            && (index + 9 == nums[i].length() || nums[i][index + 9] < '0' || nums[i][index + 9] > '9' && nums[i][index + 9] < 'A' || nums[i][index + 9] > 'Z' && nums[i][index + 9] < 'a' || nums[i][index + 9] > 'z')) {
                nums[i].replace(index, 9, "* could");
            }
            index++;
        }
        for (index = nums[i].find("I"); index < nums[i].length() && index != -1; index = nums[i].find("I", index)) {
            if ((index == 0 || nums[i][index - 1]<'0' || nums[i][index - 1] >'9' && nums[i][index - 1] < 'A' || nums[i][index - 1] > 'Z' && nums[i][index - 1] < 'a' || nums[i][index - 1] > 'z') 
                && (index + 1 == nums[i].length() || nums[i][index + 1] < '0' || nums[i][index + 1] > '9' && nums[i][index + 1] < 'A' || nums[i][index + 1] > 'Z' && nums[i][index + 1] < 'a' || nums[i][index + 1] > 'z')) {
                nums[i].replace(index, 1, "you");
            }
            index++;
        }
        for (index = nums[i].find("me"); index < nums[i].length() && index != -1; index = nums[i].find("me", index)) {
            if ((index == 0 || nums[i][index - 1] < '0' || nums[i][index - 1] > '9' && nums[i][index - 1] < 'A' || nums[i][index - 1] > 'Z' && nums[i][index - 1] < 'a' || nums[i][index - 1] > 'z') 
            && (index + 2 == nums[i].length() || nums[i][index + 2] < '0' || nums[i][index + 2] > '9' && nums[i][index + 2] < 'A' || nums[i][index + 2] > 'Z' && nums[i][index + 2] < 'a' || nums[i][index + 2] > 'z')) {
                nums[i].replace(index, 2, "you");
            }
            index++;
        }
        // 标点转换
        for (j = 0; j < nums[i].size(); j++) {
            if (nums[i][j] == '?') 
                nums[i][j] = '!';
            if (nums[i][j] == '*')
                nums[i][j] = 'I';
        }
        cout << "AI: " << nums[i] << endl;
    }
    system("pause");
    return 0;
}

7-50 最长对称子串(动态规划)

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

Is PAT&TAP symmetric?

输出样例:

11

思路:此题数据量不是太大,暴力可能不会超时,若数据量比较大的话,此类题是较为经典的动态规划类型的题目,相比之下还有中心扩展算法以及Manacher算法(马拉车算法)可供解决,这里提供动态规划算法的题解,若读者感兴趣可自行搜索了解后面两种算法的实现原理

我们可以定义二维dp布尔类型数组,对于一个子串而言,如果它是回文串,并且长度大于

2那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串“ababa”,如果我们已经知道”bab” 是回文串,那么”ababa” 一定是回文串,这是因为它的首尾两个字母都是”a”。

根据这样的思路,我们就可以利用动态规划来解决了,我们用P(I, j)代表字符串s的第i到第j个字母组成的串是否为回文串,如果子串s[i]到s[j]是回文串的话,P(I, j) = true,如果s[i]到s[j]不是回文串或者I > j的时候,P(I, j) = false。那么我们就可以写出状态转移方程:

P(I, j) = P(I + 1, j - 1) (s[i] == s[j]),对于边界条件,那就是子串长度等于二的时候,我们对其进行特殊判断即可,具体实现代码如下

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, L, n, max_len = 1;
    string s;
    getline(cin, s);
    n = s.length();
    // dp[i][j]表示字符串[i, j]区间内的字符是否是回文串
    vector<vector<bool>>dp(n, vector<bool>(n, 0));
    for (i = 0; i < n; i++) {
        dp[i][i] = true;
    }
    //先枚举子串长度
    for (L = 2; L <= n; L++) {
        //枚举左边界
        for (i = 0; i + L - 1 < n; i++) {
            j = i + L - 1;
            if (s[i] != s[j]) {
                dp[i][j] = false;
            } else {
                //若子串长度等于2时
                if (j - i < 3)
                    dp[i][j] = true;
                else 
                    dp[i][j] = dp[i + 1][j - 1];
            }
            //求最大长度
            if (dp[i][j])
                max_len = max(max_len, j - i + 1);
        }
    }
    cout << max_len << endl;


    system("pause");
    return 0;
}

7-51 月饼(贪心)

月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。

注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。

输入格式:

每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。

输出格式:

对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。

输入样例:

3 20
18 15 10
75 72 45

输出样例:

94.50

思路:对平均价格进行从大到小排序,利用贪心算法,若库存量小于市场需求量,则清空库存,否则按剩下的市场需求量进行计算即可 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, D;
    double money = 0;
    cin >> n >> D;
    vector<vector<double>>nums(n, vector<double>(3, 0));
    for (i = 0; i < n; i++) {
        // 总量
        cin >> nums[i][1];
    }
    for (i = 0; i < n; i++) {
        // 钱
        cin >> nums[i][2];
        nums[i][0] = nums[i][2] * 1.0 / nums[i][1];
    }
    sort(nums.begin(), nums.end(), [](const vector<double> &a, const vector<double> &b) {
        return a[0] > b[0];
    });
    for (i = 0; i < n; i++) {
        if (nums[i][1] <= D) {
            money += nums[i][2];
            D -= nums[i][1];
        } else {
            money += nums[i][0] * D;
            D = 0;
        }
        if (D == 0) break;
    }
    printf("%.2lf\n", money);

    system("pause");
    return 0;
}

7-52 彩虹瓶(数据结构:栈)

 彩虹瓶的制作过程(并不)是这样的:先把一大批空瓶铺放在装填场地上,然后按照一定的顺序将每种颜色的小球均匀撒到这批瓶子里。

假设彩虹瓶里要按顺序装 N 种颜色的小球(不妨将顺序就编号为 1 到 N)。现在工厂里有每种颜色的小球各一箱,工人需要一箱一箱地将小球从工厂里搬到装填场地。如果搬来的这箱小球正好是可以装填的颜色,就直接拆箱装填;如果不是,就把箱子先码放在一个临时货架上,码放的方法就是一箱一箱堆上去。当一种颜色装填完以后,先看看货架顶端的一箱是不是下一个要装填的颜色,如果是就取下来装填,否则去工厂里再搬一箱过来。

如果工厂里发货的顺序比较好,工人就可以顺利地完成装填。例如要按顺序装填 7 种颜色,工厂按照 7、6、1、3、2、5、4 这个顺序发货,则工人先拿到 7、6 两种不能装填的颜色,将其按照 7 在下、6 在上的顺序堆在货架上;拿到 1 时可以直接装填;拿到 3 时又得临时码放在 6 号颜色箱上;拿到 2 时可以直接装填;随后从货架顶取下 3 进行装填;然后拿到 5,临时码放到 6 上面;最后取了 4 号颜色直接装填;剩下的工作就是顺序从货架上取下 5、6、7 依次装填。

但如果工厂按照 3、1、5、4、2、6、7 这个顺序发货,工人就必须要愤怒地折腾货架了,因为装填完 2 号颜色以后,不把货架上的多个箱子搬下来就拿不到 3 号箱,就不可能顺利完成任务。

另外,货架的容量有限,如果要堆积的货物超过容量,工人也没办法顺利完成任务。例如工厂按照 7、6、5、4、3、2、1 这个顺序发货,如果货架够高,能码放 6 只箱子,那还是可以顺利完工的;但如果货架只能码放 5 只箱子,工人就又要愤怒了……

本题就请你判断一下,工厂的发货顺序能否让工人顺利完成任务。

输入格式:

输入首先在第一行给出 3 个正整数,分别是彩虹瓶的颜色数量 N(1<N≤10^3)、临时货架的容量 M(<N)、以及需要判断的发货顺序的数量 K。

随后 K 行,每行给出 N 个数字,是 1 到N 的一个排列,对应工厂的发货顺序。

一行中的数字都以空格分隔。

输出格式:

对每个发货顺序,如果工人可以愉快完工,就在一行中输出 YES;否则输出 NO

输入样例:

7 5 3
7 6 1 3 2 5 4
3 1 5 4 2 6 7
7 6 5 4 3 2 1

输出样例:

YES
NO
NO

思路:本题利用栈即可解答,这里需要注意栈顶是否溢出,顺序排完后栈内是否清空等问题,具体逻辑以及实现代码如下 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int i, j, k, n, m, item = 1;
    cin >> n >> m >> k;
    vector<vector<int>>nums(k, vector<int>(n, 0));
    for (i = 0; i < k; i++) {
        for (j = 0; j < n; j++) {
            cin >> nums[i][j];
        }
    }
    for (i = 0; i < k; i++) {
        stack<int>st;
        item = 1;
        for (j = 0; j < n; j++) {
            while (!st.empty() && st.top() == item) {
                st.pop();
                item++;
            }            
            if (item != nums[i][j]) {
                st.push(nums[i][j]);
            } else {
                item++;
            }
            if (st.size() > m) {
                cout << "NO" << endl;
                break;
            }
        }
        if (st.size() <= m) {
            while (!st.empty() && st.top() == item) {
                item++;
                st.pop();
            }
            if (st.empty())
                cout << "YES" << endl;
            else
                cout << "NO" << endl;
        }
    }



    system("pause");
    return 0;
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
7-8 敲笨钟 (15分) 微博上有个自称“大笨钟V”的家伙,每天敲钟催促码农们爱惜身体早点睡觉。为了增加敲钟的趣味性,还会糟改几句古诗词。其糟改的方法为:去网上搜寻压“ong”韵的古诗词,把句尾的三个字换成“敲笨钟”。例如唐代诗人李贺有名句曰:“寻章摘句老雕虫,晓月当帘挂玉弓”,其中“虫”(chong)和“弓”(gong)都压了“ong”韵。于是这句诗就被糟改为“寻章摘句老雕虫,晓月当帘敲笨钟”。 现在给你一大堆古诗词句,要求你写个程序自动将压“ong”韵的句子糟改成“敲笨钟”。 输入格式: 输入首先在第一行给出一个不超过 20 的正整数 N。随后 N 行,每行用汉语拼音给出一句古诗词,分上下两半句,用逗号 , 分隔,句号 . 结尾。相邻两字的拼音之间用一个空格分隔。目保证每个字的拼音不超过 6 个字符,每行字符的总长度不超过 100,并且下半句诗至少有 3 个字。 输出格式: 对每一行诗句,判断其是否压“ong”韵。即上下两句末尾的字都是“ong”结尾。如果是压此韵的,就按面方法糟改之后输出,输出格式同输入;否则输出 Skipped,即跳过此句。 输入样例: 5 xun zhang zhai ju lao diao chong, xiao yue dang lian gua yu gong. tian sheng wo cai bi you yong, qian jin san jin huan fu lai. xue zhui rou zhi leng wei rong, an xiao chen jing shu wei long. zuo ye xing chen zuo ye feng, hua lou xi pan gui tang dong. ren xian gui hua luo, ye jing chun shan kong. 输出样例: xun zhang zhai ju lao diao chong, xiao yue dang lian qiao ben zhong. Skipped xue zhui rou zhi leng wei rong, an xiao chen jing qiao ben zhong. Skipped Skipped
六子棋的估值函数可以通过统计某个点周围的连续子数量来计算。具体实现代码如下: ```c int evaluate(int board[][15], int player) { int score = 0; int opponent = player == 1 ? 2 : 1; int i, j, k, count; // 统计横向和纵向的连续子数量 for (i = 0; i < 15; i++) { for (j = 0; j < 15; j++) { if (board[i][j] == player) { for (k = 1, count = 0; j + k < 15 && board[i][j + k] == player; k++, count++); score += count * count; for (k = 1, count = 0; i + k < 15 && board[i + k][j] == player; k++, count++); score += count * count; } else if (board[i][j] == opponent) { for (k = 1, count = 0; j + k < 15 && board[i][j + k] == opponent; k++, count++); score -= count * count; for (k = 1, count = 0; i + k < 15 && board[i + k][j] == opponent; k++, count++); score -= count * count; } } } // 统计斜向的连续子数量 for (i = 0; i < 15; i++) { for (j = 0; j < 15; j++) { if (board[i][j] == player) { for (k = 1, count = 0; i + k < 15 && j + k < 15 && board[i + k][j + k] == player; k++, count++); score += count * count; for (k = 1, count = 0; i + k < 15 && j - k >= 0 && board[i + k][j - k] == player; k++, count++); score += count * count; } else if (board[i][j] == opponent) { for (k = 1, count = 0; i + k < 15 && j + k < 15 && board[i + k][j + k] == opponent; k++, count++); score -= count * count; for (k = 1, count = 0; i + k < 15 && j - k >= 0 && board[i + k][j - k] == opponent; k++, count++); score -= count * count; } } } return score; } ``` 其中,`board`是一个二维数组表示棋盘,`player`表示当前玩家(1为先手,2为后手)。函数返回当前棋盘在`player`下的估值分数。该函数会统计当前棋盘中所有的连续子,将连续子的数量平方加入到分数中。如果连续子属于对手,则减去分数。最终返回的分数越高表示当前棋盘在`player`下越有优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值