PAT 7.3

PAT 乙级真题 Day 1

致谢:本博客参考了 @柳婼 的PAT乙级题解,清晰的算法思路、简洁的代码给了我很大启发。

完成题目:1001~1005

第一天写的时候C++还没有完全安装好,一部分代码通过Python完成,所以没有涉及到很复杂的语法。

1002 写出这个数

题目描述:

读⼊⼀个⾃然数n,计算其各位数字之和,⽤汉语拼⾳写出和的每⼀位数字。

输入格式:

每个测试输⼊包含1个测试⽤例,即给出⾃然数n的值。这⾥保证n⼩于10100。

输出格式:

在⼀⾏内输出n的各位数字之和的每⼀位,拼⾳数字间有1 空格,但⼀⾏中最后⼀个拼⾳数字后没有空格。

输入样例:
1234567890987654321123456789
输出样例:
yi san wu

题目分析:

  1. 此题最简单的思想是,对于输入的每一个数x,通过switch语句进行判断,将0~9所有10种情况写入case语句中。
  2. 通过对string类的正确使用,可以简化代码,具体思路如下:
  • 将输入变量定义为string类,以方便处理。
  • 创建字符串数组string number[10],用来储存0~9的汉语拼音表示。
  • 遍历输入的字符串的每一位,统计所有数位之和sum。
  • (注意此处的方法)将int型变量sum进行类型转换:
    string num = to_string(sum) , 此时num为string类,方便遍历。
  • 遍历num,与我们之前定义的number[10],由标签一一对应,例如:如果我们读到num[i],是char类型数值,则(num[i]-‘0’)即可获取到对应的汉语拼音表示。
  1. 本题在C++上需要注意的是int型变量向string转换时,需要的to_string(int)方法,对于编程来说,需要注意巧妙地建立数组下标与对应元素之间的关系。

1003 我要通过!

题目描述:

“答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。

得到“答案正确”的条件是:

  1. 字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符;
  2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
  3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a、 b、 c 均或者是空字符串,或者是仅由字母 A 组成的字符串。

    现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。
输入格式:

每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 n (<10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。

输出格式:

每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出 YES ,否则输出 NO

输入样例:
9
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
APT
输出样例:
YES
YES
YES
YES
NO
NO
NO
NO
NO

题目分析:

观察输出结果为正确的字符串,可以知道,输出为“答案正确”的条件是,

  1. 输入为"xPATx"的字符串,其中x为A或空字符串,A的个数从0到n不限。
  2. 如果aPbTc正确,那么aPbATca也正确:

    写出如下正确的例子:

    PAT

    APATA

    AAPATAA --> APAATAA

    AAAPATAAA --> APAAATAAA

    AAPAATAA --> AAPAAATAAAAAA

    可以看出,字符串的正确性可以递归定义,最简单的形式是xPATX,将xPATx拓展为aPbTc,推出aPbATca的形式,通过改变b所代表的A的个数,定义新的字符串。

    其中,P、T中间及两侧的字符串满足如下关系:

    结尾 = 开头*中间
  3. 算法思路

    对于任意输入的字符串s,判断条件为:
  • P和T的个数均为1
  • 字符A的个数不为0(至少应为PAT)
  • 满足仅有3个字符:P、A、T
  • 满足结尾=开头*中间的关系
  1. 代码实现上的思考(借鉴了其他博主的教程)
  • 在输入变量时,我们定义输入变量为string类型,这样方便了变量的按位读取以及长度计算
  • 在统计时,我们定义C++的 map<char,int> 容器,这样比数组处理更加方便,同时可以根据容器m的大小来判断是否有多余的字母:当容器大小仅为3时,说明仅有PAT三个字母。
  • 关于容器的具体方法,参考如下:
for(int i = 0; i < n; i++) {
 cin >> s;
 map<char, int> m;
 for(int j = 0; j < s.size(); j++) {
 m[s[j]]++;
 if (s[j] == 'P') p = j;
 if (s[j] == 'T') t = j;
 }
 if (m['P'] == 1 && m['A'] != 0 && m['T'] == 1 && m.size() == 3 && t-p
!= 1 && p * (t-p-1) == s.length()-t-1)
 printf("YES\n");
 else
 printf("NO\n");

1005. 继续(3n+1)猜想 (25)

题目描述:

卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题⽬⾥,情况稍微有些复杂。
当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每⼀个数。例如对n=3进⾏验证的时候,我们需要计算3、5、8、4、2、1,则当我们对n=5、8、4、2进⾏验证的时候,就可以直接判定卡拉兹猜想的真伪,⽽不需要重复计算,因为这4个数已经在验证3的时候遇到过了,我们称5、8、4、2是被3“覆盖”的数。我们称⼀个数列中的某个数n为“关键数”,如果n不能被数列中的其他数字所覆盖。

现在给定⼀系列待验证的数字,我们只需要验证其中的⼏个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从⼤到⼩的顺序输出它们。

输⼊格式:

每个测试输⼊包含1个测试⽤例,第1⾏给出⼀个正整数K(<100),第2⾏给出K个互不相同的待验证的正整数n(1<n < = 100)的值,数字间⽤空格隔开。

输出格式:

每个测试⽤例的输出占⼀⾏,按从⼤到⼩的顺序输出关键数字。数字间⽤1个空格隔开,但⼀⾏中最后⼀个数字后没有空格。

输⼊样例:
6
3 5 6 7 8 11
输出样例:####
7 6

题目分析:

  1. 本题类似于数据结构课中学到的哈希函数,即一个数x通过哈希函数,对应一系列哈希数y,这些y被x“覆盖”。
  2. 显然,如果要表示“是否需要验证”,则我们需要构建一个大小为1000的数组,来表明每个数是否被验证过。
  3. 对于每个数n,我们的思路是:
  • 判断n是否被访问过,若没有访问过,则向下一步执行。
  • 如果n为奇数, n = 3*n+1;如果n为偶数, n = n/2;
  • 当更新后的n被访问过,即被映射到同一个值时,停止循环验证,
  • 标记更新后的n的位置为访问过。
  1. 上述算法的合理性在于,当 n = 1时,一定会在n = 1停止,当 n = 2时,一定会在n=1停止,当n=3时,一定会在n = 2停止,在n不断的更新中将访问过的数字的个数不断扩充,构建覆盖关系。这些数存在覆盖关系,可以由一个数访问到,所以不必验证。
  2. 对于没有被访问过的数,即为题目要求的“关键数”,我们输出即可。
  3. 在数学上,这些没有被访问过的“关键数”不存在覆盖关系,类似于离散数学中哈斯图的“极大元”与“极小元”的概念,此题画出哈斯图后会更清晰。
  4. 在代码实现上,对于数字的存储,我们采用 C++的vector形式,这样处理起来较数组更方便,而对于降序排列,我们采用 C++内置的sort() 函数,又因为sort()默认是升序,所以前我们需要在sort的参数中自定义compare函数为降序。
  5. 关键代码如下:
vector<int> v(k);
 for (int i = 0; i < k; i++) {
 cin >> n;
 v[i] = n;
 while (n != 1) {
 if (n % 2 != 0) n = 3 * n + 1;
 n = n / 2;
 if (arr[n] == 1) break;
 arr[n] = 1;
 }
 }
 sort(v.begin(), v.end(), cmp);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值