PAT乙级 1005

158 阅读3分钟

题目如下: image.png 这是1001 3n+1的进阶版。这里是3n+1猜想:

image.png 我们来比较一下二者的不同,1001就是简单的验证3n+1,求其中到达一的步数。而1005提出了一个新的概念:关键数字。好,那么重点来了,关键数字是什么?这里根据题目条件,比如当你验证5的猜想时 5 8 4 2 1,那么接下来当你需要验证8 4 2时,可以直接得出满足条件,因为你在之前验证5时的过程中出现了这几个数。但是如果没有出现,那么你不得不判断一下这个数是否满足猜想,比如7。

了解之后我们来看这个题,要求输入一段数字。寻找其中的关键数字,我第一次的代码如下,思路都在注释,比较清晰了:

//为了避免重复计算,可以记录下递推过程中遇到的每一个数
//每个测试输入包含 1 个测试用例,第 1 行给出一个正整数 K (<100),第 2 行给出 K 个互不相同的待验证的正整数 n (1<n≤100)的值,数字间用空格隔开
//你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

//数组开大
int s[100005];//s记录数组.被记录记为1,未被记录为0
int a[100005];//记录关键数字的数组,要进行排序(从大到小
int cnt;

//比较函数用
bool cmp(int a, int b) {
    return a > b;//从大到小
}

/*/判断是否是最后一个
int check_flag(int n){
    if(n != 1) return 1;
    else return 0;
}*/

//判断输出空格 
void check_space(int flag,int cnt){
    cout << a[flag];//输出

    if(flag != cnt - 2) cout << " ";//不是最后一位,那么输出空格
}

//验证3n+1    
void check(int num){
    //3n+1猜想
    int step = num;//一个替身
    s[num] = 1;//num开始验证,标记先标记
    while(step != 1){

        if(step % 2 == 0) step /= 2;//偶数
        else step = (3 * step + 1) / 2;//奇数

        s[step] = 1;//被记录,标记一次
        } 
}

int main(){
    int n,num;//
    cin >> n;
    while(n --){
        
        cin >> num;

        if(s[num] == 0){
            a[cnt ++] = num;//未在s数组中标记即这个num是关键数字,存在a数组中
            check(num);//关键数字进行验证,但是第一个输入的数字不是
        }
    }

    //因为第一个输入验证猜想的数不是关键数字,此时他在as数组的第一位,我们将他移除
    //此时关键数字数量为cnt-1
    for(int i = 0;i < cnt; i++){
        a[i] = a[i + 1];
    }
   
    sort(a, a + cnt - 1, cmp);//从大到小排序

    //for(int i = 0; i < cnt - 1; i ++) cout << a[i] << endl;
    //cout << cnt - 1 << endl;

    for(int i = 0; i < cnt - 1; i ++){
        check_space(i,cnt);
    }

    cout << endl;
    return 0;
}
//一行中最后一个数字后没有空格

/*
6
3 5 6 7 8 11
*/

虽然可以过部分测试点,但毫无疑问是错的,我想了很久不,在朋友的提醒下找到了错误。那就是 //因为第一个输入验证猜想的数不是关键数字,此时他在as数组的第一位,我们将他移除 //此时关键数字数量为cnt-1 for(int i = 0;i < cnt; i++){ a[i] = a[i + 1]; }这一段代码,其中有一点是样例中第一个输入的3不是关键数字,所以我直接在记录数组排序前将第一个数也就是输入的3给剔除了。但是出现了很严重的错误,那就是谁说得第一个就不能说关键数字呢?代码逻辑出现了错误,之所以出现这个错误,我发现是我的理解吧出现错误,他的关键数字是针对整个数字,后面有一个数字的验证覆盖了前面的数字,那么前面的数字就不是关键数字了。他并不是按照输入顺序来的。那么一切重来,但基本的思想不变,那就是用一个数组去记录这个数字被覆盖了没有,用不用自己去验证。先不对数组做任何处理,所有的都循环完之后,再去寻找没有被标记的数组位置,那就是关键数字了,代码如下:

//为了避免重复计算,可以记录下递推过程中遇到的每一个数
//每个测试输入包含 1 个测试用例,第 1 行给出一个正整数 K (<100),第 2 行给出 K 个互不相同的待验证的正整数 n (1<n≤100)的值,数字间用空格隔开
//你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

//数组开大
int b[100005];//存输入的数组
int s[100005];//s记录数组.被记录记为1,未被记录为0
int a[100005];//记录关键数字的数组,要进行排序(从大到小
int cnt;

//比较函数用
bool cmp(int a, int b) {
    return a > b;//从大到小
}

/*/判断是否是最后一个
int check_flag(int n){
    if(n != 1) return 1;
    else return 0;
}*/

//判断输出空格 

//验证3n+1    
void check(int num){
    //3n+1猜想
    int step = num;//一个替身
    while(step != 1){

        if(step % 2 == 0) step /= 2;//偶数
        else step = (3 * step + 1) / 2;//奇数

        s[step] = 1;//被记录,标记一次
        } 
}

int main(){
    int n;//
    cin >> n;
    
    for(int i = 0; i < n; i ++)
        cin >> b[i];//输入数组

    for(int i = 0; i < n; i ++){
        int temp = b[i];
        check(temp);//检查验证
    }

    //sort(b,b + n,cmp);

    for(int i = 0; i < n; i++){
        if(!s[b[i]])
            a[cnt ++] = b[i];//关键数字存进去
    }

    sort(a,a + cnt,cmp);
    
    cout << a[0];
    for(int i = 1; i < cnt; i ++)
    cout << " " << a[i];

    return 0;
}


//一行中最后一个数字后没有空格

/*
6
3 5 6 7 8 11
*/

成功AC,困扰我一天的题目被解决了,真是可以被让人放心睡觉,眠了。 题目链接