[PAT Basic Level] 1023~1028

1023. 组个最小数

题目分析:

这道题没有遇到需要额外注意的坑。要求组成的数字尽量小,那么第一位应该是非0的且出现过的最小数字,之后再从0开始,若该位出现次数不为0,则输出,一直遍历到9。
编程的时候有个地方要注意,输出了最高位之后,要记得把储存该位数字出现的次数相应减1。

源代码

#include <stdio.h>

int main()
{
    int digit[10];  //储存各个数字的个数
    int num[50];  //储存生成的数字
    for(int i=0;i<10;++i) scanf("%d",&digit[i]);
    for(int i=1;i<10;++i)
        if(digit[i]){
            num[0]=i;  //找到非0的最低位作为首位
            digit[i]--; //相应剩下数字个数-1
            break;
        }
    int count=1; //记录生成数的位数
    for(int i=0;i<10;++i){  //从最小数开始
        while(digit[i]--)  //当该位数不为0
            num[count++]=i;
    }
    for(int i=0;i<count;++i)
        printf("%d",num[i]);
    return 0;
    
}

1024. 科学计数法

题目分析:

细节比较多,但没有额外的数据测例和格式上的坑。耐心+细心就能顺利AC。
在保存各位有效数字的时候,直接开数组保存各位有效数字即可,不必保存小数点,因为第一位必是整数位,后面全是小数位。
在输出时,需要根据各种情况进行判断。

  • 指数大于等于小数位数:无小数点。
  • 指数不够小数位数,但指数大于-1:整数位不为0,输出相应整数位后打印小数点,再输出剩下的位数
  • 指数小于0:前面两种情况下,有效数字第一个元素都输出了,这种情况下有效数字都还没输出,另外还需考虑前面补零的情况,补充零的个数=1-exp (exp指存储的指数值)

源代码

#include <stdio.h>

int main()
{
    char numSymbol;  //储存数字和指数的符号
    numSymbol=getchar();
    char *digit=new char[10000]; //储存各个有效数字
    digit[0]=getchar();
    getchar(); //读取并丢弃小数点

    int count=1;  //记录数字总位数
    while((digit[count++]=getchar())!='E');  //直到遇到符号E
    count--;   //丢弃字符E,存储的小数位数为count-1
    int exp;
    scanf("%d",&exp);  //读取指数
    if(digit[0]=='0'){ printf("0");return 0;}  //若首位为0,输出0
    if(numSymbol=='-') printf("-");  //若是负数且首位不为0,打印符号
    if(exp>=count-1){  //若实际是整数
        for(int i=0;i<count;++i)
            printf("%c",digit[i]);
        for(int i=0;i<exp-count+1;++i)
            printf("0");   //剩余指数补0
    }
    else{  //否则是小数
        if(exp>-1){  //整数部分非0
            int intDigit=exp+1;  //普通表示法下整数位数
            for(int i=0;i<intDigit;++i)  //输出整数部分
                printf("%c",digit[i]);
            printf(".");
            for(int i=exp+1;i<count;++i) //剩余小数
                printf("%c",digit[i]);
        }
        else{ //否则整数位为0
            int extraZero=-exp-1; //小数点后需补上的的0个数
            printf("0.");
            for(int i=0;i<extraZero;++i)
                printf("0");
            for(int i=0;i<count;++i)
                printf("%c",digit[i]);
        }
    }

    delete []digit;
    return 0;
}

1025. 反转链表

题目分析:

题目乍看起来比较麻烦,虽说是链表,但肯定不能用链表来写,只是套用了链表的思想。但是注意到题目所给的内存限制非常宽松,为了加快查找速度,应该选择trade memory for speed,开一个大数组存储各个结点,采用随机访问的方式来缩短寻址时间。

这里我用了结构体来表示各个结点,成员包括了地址、数据和下一元素的地址。

反转链表时,可以直接用algorithm头文件中的reverse函数,这个函数需要提供起始位置和终止位置,其工作原理就是从第一个元素开始反复调用swap(begin+i,end-i)。还有一个简化代码的策略,不要去修改反转后的每个结点对应的下一元素变量的值,在输出本节点对应的下一结点地址时不使用本结点的成员next,而直接用下一结点的成员address来输出。这样对于最后一个元素未被换位置时显然可行,但对于最后一个元素恰好被调换位置的情况似乎会出现问题。为了解决这个麻烦,可以在循环输出时留下最后一个元素,在输出最后一个元素的下一结点地址时直接输出-1即可

源代码

#include <stdio.h>
#include <algorithm>

struct  linkNode
{
    int _address,_data, _next;
};
int main()
{
    linkNode* nodeArray=new linkNode[100000];  //牺牲一些空间,采用随机访问方式
    int head,nodeNumber,gap;  //首节点地址、结点数、反转步长
  
    scanf("%d %d %d",&head,&nodeNumber,&gap);
      linkNode* linkList=new linkNode[nodeNumber];
    int address;
    for(int i=0;i<nodeNumber;++i){
        scanf("%d",&address);
        nodeArray[address]._address=address;
        scanf("%d %d",&nodeArray[address]._data,&nodeArray[address]._next);
    }
    int count=0;  //统计链表中的数字个数,可能存在某些元素不在链表中的情况
    int next=head;
    while(next!=-1){
        linkList[count++]=nodeArray[next];
        next=nodeArray[next]._next;
    }
    for(int i=0;i<(count-count%gap);i+=gap)  //对若干组长度符合要求的进行逆序
        std::reverse(linkList+i,linkList+i+gap);
    for(int i=0;i<count-1;++i)  //交换后唯一需要修改输出的就是下一元素地址,这里不改变结构体的值,直接输出物理上下一元素地址即可
        printf("%05d %d %05d\n",linkList[i]._address,linkList[i]._data,linkList[i+1]._address);
    printf("%05d %d -1",linkList[count-1]._address,linkList[count-1]._data);

    delete[]nodeArray;
    delete[]linkList;
    return 0;
}

1026. 程序运行时间

题目分析:

相比前面的两道题这题要容易不少,考虑好四舍五入方法得到正确的秒数后,剩下的问题就能迎刃而解。
计算秒数首先要注意要显式将时间差转化成double型再除以10,否则编译器会认为是整型除法,计算得到整数结果再扩展成double值,那么这个double表示的还是一个整数。
为了实现四舍五入,直接转换成int肯定不行,这样只是单纯丢弃小数部分。可以用准确的double值减去转换成整形的int值,二者的差乘以2再转换成int,用代码表示为:

double realSecond; //计算得到的准确的秒数
int second=realSecond;  //截断得到的不精确秒数
second+=(int)2*(realSecond-second); //四舍五入后的秒数

源代码

#include <stdio.h>

int main()
{
    int clock1,clock2;
    scanf("%d %d",&clock1,&clock2);
    int diff=clock2-clock1;
    double rawSeconds=(double)diff/100;
    int seconds=rawSeconds;  //转换成秒数
    seconds+=(int)2*(rawSeconds-seconds);  //四舍五入
    int hour=seconds/3600;
    seconds=seconds%3600;
    int minute=seconds/60;
    seconds=seconds%60;
    printf("%02d:%02d%02d",hour,minute,seconds);
    return 0;
}

1027. 打印沙漏

题目分析:

这题有个大坑点:千万不要输出符号右边的空格,即每行左边按要求空格之后输出完符号就换行。
抛开这个坑,剩下就是分析清楚数学关系,根据题目的条件容易分析得到单侧沙漏各层数据为一个等差数列,假设从第一层到中间一个符号共有n层,那么整个沙漏消耗的符号数为:
N u m = 2 ∗ [ 1 + 3 + 5 + . . . + ( 2 n − 1 ) ] − 1 = 2 n 2 − 1 Num=2*[1+3+5+... +(2n-1)]-1=2n^2 -1 Num=2[1+3+5+...+(2n1)]1=2n21
根据这个式子循环遍历寻找能够消耗的最大符号数,那么就可以求出层数以及剩下的未用符号数量。
根据格式,计算好每一行要输出的空格个数和符号个数,输出即可。

源代码


#include <stdio.h>

int main()
{
    int num;
    char symbol;
    scanf("%d %c",&num,&symbol);
    int count=0; //统计最多能组成沙漏数的n值
    int sandNumber=1;
    while(sandNumber<=num){
        count++;
        sandNumber=2*(count+1)*(count+1)-1;
    }
    int left=num-(2*count*count-1); //剩余未用符号数量
    int halfLevel=count-1; //对称部分层数
    int width=2*halfLevel+1;  //输出宽度
    int tmp=halfLevel;
    while(tmp){  //上半部分
        int len=2*tmp+1;
        int space=(width-len)>>1; //输出空格数
        for(int i=0;i<space;++i) printf(" ");
        for(int i=0;i<len;++i)   printf("%c",symbol);
        printf("\n"); //换行
        tmp--;
    }
    for(int i=0;i<=halfLevel;++i){  //下半部分
        int len=1+2*i;
        int space=(width-len)>>1;
        for(int j=0;j<space;++j) printf(" ");
        for(int j=0;j<len;++j)   printf("%c",symbol);
        printf("\n");  //换行
    }
    printf("%d",left);

}

1028. 人口普查

题目分析:

有两点需要注意,不算坑,确实是稳健的代码所需要考虑到地方,也是我几次失败修反复修改测试才发现:

  1. 即使年纪最大的人就是年纪最轻的人,也要把名字都输出出来,而不能只输出一遍。
  2. 输入可能没有符合要求的人,这时候输出0即可

为了保存信息,我采用了一个结构体,存储姓名、出生年、月、日。
之后在判断人数和寻找年龄最大最小者时,我采用的方法是,先把所有输入信息存入数组,然后按照年龄降序进行排序。之后再遍历,找到的第一个年龄符合要求的人即是年龄最大者,最后一个年龄符合要求的人即是年龄最小者。排序算法建议采用algorithm头文件里的sort函数,自己编写一个判断函数做第三个参数即可,比自己手撸方便多了。

不过这种方法在没有人生日有效的情况下需要注意判断。

源代码

#include <stdio.h>
#include<algorithm>
using namespace std;
struct info
{
    char _name[6];
    int _year,_month,_day;
};

bool cmp(const info &p1,const info& p2);
int main()
{
    int num;
    scanf("%d",&num);
    info *person=new info[num];
    int count=0;  //记录生日有效人数
   
    for(int i=0;i<num;++i) {
        scanf("%s %d",person[i]._name,&person[i]._year);
        getchar(); //读取丢弃"/"
        scanf("%d",&person[i]._month);
        getchar();
        scanf("%d",&person[i]._day);
    }
    sort(person,person+num,cmp);  //按年龄降序序排列
    int i=0;
    while(person[i]._year<=1814&&i<num){
        if(person[i]._year==1814){
            if(person[i]._month>9) break;
            else if(person[i]._month==9&&person[i]._day>=6) break;
        }
        i++;
    }  //循环结束时i对应的即为年龄最大者,也可能仍然非有效年龄
    int old=i;
    if(i<num){
        while(person[i]._year<=2014&&i<num){ //寻找年龄最小者
            if(person[i]._year==2014){
                if(person[i]._month>9) break;  //未出生
                else if(person[i]._month==9&&person[i]._day>6) break;
            }
            count++;
            i++;
        }
    }
    if(count)printf("%d %s %s",count,person[old]._name,person[i-1]._name);  //不论二者是不是同一个人,均输出其名字
    else printf("0");
    return 0;
}
bool cmp(const info &p1,const info& p2)
{
    if(p1._year!=p2._year) return p1._year<p2._year;  //按年龄降序排列,因此年月日升序
    if(p1._month!=p2._month) return p1._month<p2._month; 
    return p1._day<p2._day;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值