蓝蓝计算机考研算法-day03小球走过距离和自动判定程序

303 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情

Day03 2023/03/01

难度:中等

题目1

一个小球从10处落下,每次的弹回之前的高度一半,请问小球在静止之前一共走过多少距离?

题目2

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

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

1.字符串中必须仅有P、A、T这三种字符,不可以包含其它字符;

2.任意形如PATx的字符串都可以获得“答案正确”,其中x或者是空字符串,或者是仅由字母A组成的字符串:

3.如果aPbTc是正确的,那么aPbATca也是正确的,其中a、b、c均或者是空字符串,或者是仅由字母A组成的字符串。

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

输入格式:

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

输出格式:

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

运行实例

image.png

思路一


  • 根据题目一,我们通常可以选择递归或者循环的方式,这里我选择循环的方式,因为递归的空间复杂度会更高一些。本题比较简单,就不写详细的步骤了,代码注释写的比较详细,仔细看一下应该就没问题了!!!

思路二


题目二稍有难度,我在传统解法的基础上,优化了一下代码数量减掉了不少,看起来比较简洁,但是代码的理解难度也会随之提高一些,需要稍微琢磨一下。下面我就从题目开始一步一步分析:

  • 首先题目中的条件1和条件2比较好理解,综合一下就是字符串中仅含P,A,T这三种字母,并且最短且符合条件的字符串为PAT,像PT这样的就不符合条件。
  • 条件三相比就更难理解一些,接下来细讲条件三:
    1. 条件三说如果aPbTc是正确,很多人就不明白"如果"了,这就用到条件二了,因为条件二可以确定100%答案正确的字符串。由条件二可以知道像PAT APATA AAPATAA AAAPATAAA ……都是正确的所以我们由条件二和条件三可以推出一些规律。
    2. 接下来我用条件三拿①PAT举个例子,重点来了:如果aPbTc=PAT,那么a=空,b=A,c=空,带入aPbATca得PAAT正确,如果PAAT正确,那么令PAAT=aPbTc,得a=空,b=AA,c=空,带入aPbATca得PAAAT正确,依次带下去可得PAAAA……T都是正确的。
    3. 下面找一下规律。 aa908321583160e425c561fec043045.jpg 第一个数字代表P之前A的个数,第二个数字代表P和T之间A的个数,第三个数字代表T之后A的个数,观察可得:第一个数字*第二个数字=第三个数字。(核心观点)

关键点


  • 题目二的代码中最后的if判断条件比较多下面解释一下:c == '\n'保证了T后面没有其他字母, pos==2保证了PT之间没有其他字母,其中第一个if中else break保证了P前面没有其他字母,三个条件综合保证了一定符合条件一和条件;record_A[1]!=0 ,保证PT之间一定存在字符A,最后record_A[2]==record_A[1]*record_A[0]保证了符合上面找到的规律(即满足条件三)

算法实现


c++代码实现-优化后

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

// 自动判定输入的字符串是否符合正确答案要求
void AutoDetermine() {
  int num = 0;  // 待判定字符串个数                    
  cin >> num;   // 输入n值                           
  char c;       // c用来获取当前待判定字符串中的字符(单个单个的获取)
  while (getchar() != '\n');               // 吸收掉n之后的第一个回车
  for (int i = 0; i < num; i++)            // 循环判定每num个字符串是否符合要求
  {
    int pos = 0, record_A[3] = {0, 0, 0};  // pos为record_A数组当前下标 record_A用来记录字符A在三个不同位置出现的次数
    while (( c = getchar()) != '\n')       // 判定输入的字符,用来记录各个A在不同位置出现的次数
    {
      if (c == 'A')                  record_A[pos]++;  // record_A[0]  P之前的A的数量
      else if (c == 'P' && pos == 0) pos=1;            // record_A[1]  P与T之间的A的数量
      else if (c =='T' && pos == 1)  pos=2;            // record_A[2]  T之后的A的数量
      else                           break;            // 不满足条件一直接跳出循环
    }
    if(c == '\n'&& pos == 2 && record_A[1] && record_A[2] == record_A[1]*record_A[0])   // 在关键点处给出了详细对条件的解释,不懂得可以回头看一下
    cout << "YES" << endl;                                                
    else  cout << "NO" << endl;                                            
    if(c!='\n') while(getchar() != '\n');  //当不满足的时候吸收掉下一个回车,防止下次循环直接跳过                                             
  }                               
}

int main() {
    cout << "题目1测试:" << endl;
    float high = 10, second = 0, sum = 0;  // high为小球每次即将落下时的高度,second为每次弹起的高度,sum为小球做过的距离和
    while (high) {                         // 当high为0时停止循环
        second = high / 2;                 // 每次弹起的高度为每次落下高度的一半
        sum += (high + second);            // 做累加
        high /= 2;  //更新high值
    }
    cout << "小球静止前一共走过:" << sum << "米" << endl << endl;

    cout << "题目2测试:" << endl;
    cout << "请输入待判定字符串的个数和待判定的字符串,每个数据各占一行,用回车隔开:" << endl;
    AutoDetermine();  // 调用
}

题目一:

  • 时间复杂度 O(n)O(n)--- n为最终循环的次数
  • 空间复杂度 O(1)O(1)--- 仅有常数级的变量,没有额外的辅助空间

题目二:

  • 时间复杂度 O(nm)O(n*m)--- 外层循环n次,内层每次判断一个字符最坏情况下循环m次,其中n为字符串个数,m为字符串长度
  • 空间复杂度 O(1)O(1)--- 仅有常数级的变量,没有额外的辅助空间

总结

  • 题目二有一定难度,主要是条件三的分析,其次按照传统的写法,代码量会比较大,上述的方法经过优化过后代码量已经减少了很多,虽然看起来比较简洁,但是代码中很多细节点,其中关于'/n'的细节点,最好是自己dbug一下去看代码是如何运行的。