PAT(Basic Level)总结(1020~1038)

前言

这是第2篇博客来总结PAT Basic Level 的1020~1038题。



解题关键词

分类讨论、STL的简单使用(C with STL)、分类的sort排序、哈希表。


1020

月饼 (25 分)

总结:
1.声明一个结构体存储一种月饼信息:库存、总价、单价,调用sort函数对月饼单价进行排序。
2.为使得收益最大化,采用一种简单的贪心算法:按价格从高到低依次卖出月饼,若高价格月饼库存不够,再卖出低价月饼。
注意:单价 = 总价/库存,可能出现小数,因此方便起见三个变量都声明为double类型。后续计算中最大销售额也要声明为double类型。

1021

个位数统计 (15 分)

总结:**散列表(Hash table)**的简单应用。声明一个int类型的数组来统计0~9出现的次数。

1022

D进制的A+B (20 分)

总结:模拟2~10进制的计算方法。
需注意两点:
(1)每次A+B对D取模后,最先得到的数字是转换后的低位,最后得到的数字是转换后的高位。
(2)使用do while循环,而不是while循环。若使用while循环,当十进制数为0时会不进入循环,不能输出0。

1023

组个最小数 (20 分)

总结:首先输出除0以外最小的数(1~9),作为首位;之后按从0到9的顺序,依次输出所有数。第1个for循环遍历1到9;第2个for循环遍历0到9。

1024

科学计数法 (20 分)

总结:首先是找到E的位置,E之前的数是数值(有效位数),E之后的数是指数。

1025

反转链表 (25 分)

总结:此题是用数组模拟链表的实现和操作。
首先声明一个结构体数组表示一个“内存空间”,来存储各个节点的信息:

struct node
{
    int address;			//节点的地址 
    int data;
    int next_address;		//下一个节点的地址
    int order = 100000;		//此节点是第几个节点,初始为最后一个
}node_list[100000];

从0开始标注每个节点的位置,并按照order从小到大的顺序(cmp_descending函数)sort排序各个节点。

    int n = 0, pt = Init_addr;      
    while(pt != -1)
    {
    	node_list[pt].order = n;
    	n++;                        //统计有效节点的个数
    	pt = node_list[pt].next_address;
	}
	sort(node_list, node_list+100000, cmp_descending);

此时正式开始反转链表,每K个元素进行反转,调用sort函数按照从大到小的顺序(cmp_ascending)排列各节点。注意:不足K个的子列不需要反转;以及for循环的边界条件。

    for(int i=0; i+K<=n; i+=K)
    {
        sort(node_list+i, node_list+i+K, cmp_ascending);
    }

输出反转后的链表。

    for(int i=0; i<n-1; i++)	//反转后节点的next_address会发生变化: 变为 node_list[i+1].address
	{
		printf("%05d %d %05d\n", node_list[i].address, node_list[i].data, node_list[i+1].address);	
	}
	printf("%05d %d -1", node_list[n-1].address, node_list[n-1].data);

1026

程序运行时间 (15 分)

总结:考察四舍五入。由于机器时钟是每秒打点100次,将两次打点数的差转化为秒,进行如下操作:

    if((C2-C1)%100>=50)
        c = (C2-C1)/100+1;
    else
        c = (C2-C1)/100;

1027

打印沙漏 (20 分)

总结:分别打印上下两个三角阵,只需打印前置空格,不用考虑后置空格。通过对(N/2)开方可得一个三角阵的行数(包括仅有一个字符的中心处的一行),从上到下依次打印两个三角,注意中心处的一个字符不要重复打印。
注:此题的测试点1是只输入一个字符,此种情况单独处理。

1028

人口普查 (20 分)

总结:首先声明一个结构体数组存储每个人的姓名和出生年月日,另外在结构体中包含一个有效标志,表明生日的有效性。然后依次判断年月日的有效性;接着使用sort函数对结构体数组排序。
cmp函数依次判断日期有效性、年、月、日

bool cmp(people a, people b)
{
    if(a.valid != b.valid)
        return a.valid > b.valid;
    else 
    {
        if(a.year != b.year)
            return a.year<b.year;
        else
        {
            if(a.month != b.month)
                return a.month<b.month;
            else
                return a.day<b.day;
        }
    }
}

1029

旧键盘 (20 分)

总结:输入的两个字符串中,有字符缺失的字符串s2作为参照,遍历完整字符串s1的每一位,每次遍历时将s1的小写转化为大写。如果两个字符串的某位出现不同,则把s1的该位存储到一个absent数组中。

    int i=0, j=0, num=0;
    while(s1[i] != '\0')			//s1为完整序列
    {
        if(s1[i] != s2[j])			//s2为残缺序列
        {
            if(s1[i]>='a'&&s1[i]<='z')
                s1[i] = s1[i]-32;   //小写转化为大写
            if(flag[s1[i]] == false)
            {
                flag[s1[i]] = true;
                absent[num++] = s1[i];
            }
            i++;                    //切换到输入序列的下一个字符
        }                           //s2序列的字符不发生变化
        else
        {
            i++;
            j++;
        }
    }

1030

完美数列 (25 分)

总结:这道题其实考的是一个思维定势:虽然输入数列经过升序排序后,最大数前面的数是最多的,但这些数不一定能构成一个完美数列。 比如给定输入数列:1,1,1,1,2 且p=1。此时前4项构成了完美数列,并非是5项,因此最多可以选择4个数。
思路:首先将数列进行升序排序;然后进行二维遍历(需进行优化,避免O(n的平方)超时)。
优化方法:使用count累加记录个数

    int count = 0;
    for(int i=0; i<N; i++)
    {
        //嵌套循环进行优化: 内层j循环用来遍历数组元素并比较
        //每次遍历一遍数组,获得一个count值:得到一个以当前首元素(0,1,...)的数组(含有count个元素)
        //下次遍历时,会更新一个首元素(i的外层循环)
        for(int j=i+count;j<N;j++)  //j=i+count避免每次从头遍历数列
        {
            if(arr[j]>arr[i]*p) 	//不满足关系式,跳出内层循环
                break;
            else    				//满足关系式,
            {
                if(j-i+1>count)      
                count=j-i+1;		//计数+1是包含首元素
            }
        }
    }

1031

查验身份证 (15 分)

总结:建立一个映射表,数值0~10对应于相应的字符,判断该字符是否与身份证号的第18位校验位相同,相同则通过;不同则输出此身份证号。

1032

挖掘机技术哪家强 (20 分)

总结:又是一道结构体+sort排序找第一名的题。
思路:
1.首先声明一个结构体数值存储每个学校的编号、总分和一个标志位(指示该校是否出现过);
2.接着依次输入存储学校的编号和成绩;
3.最后调用sort函数排名。
注:测试点2是输入的n个学校都不相同(编号1~n),因此可声明一个大小为n+1的数组来存放各个学校的信息。

1033

旧键盘打字 (20 分)

总结:和1029互为“逆运算”,此题是告知坏的键位和待输入的字符串,求输出的残缺字符串。
思路:
1.首先声明一个哈希表(大小128的char类型数组)记录缺失的键位(注:英文字母的坏键以大写给出,因此涉及大写转小写后存储在哈希表中);另外声明一个标志位记录’+‘是否缺失。
2.遍历输入的字符串,判断是否输出。一是遇到在哈希表中被记录的字符则不输出;二是输入为大写字母,且此字母键未坏但‘+‘坏了,则不输出此字母。

1034

有理数四则运算 (20 分)

总结:
1.此题需要实现有理数的四则运算,也就涉及到分数的四则运算,声明一个结构体存储分数的分子和分母;
2.另外题目对输出格式要求较高,分数要求是真分数和最简分数,两重因素使得代码量略大。
涉及知识:求最大公约数;分数化简。

long gcd(long a, long b)	//递归的方法求最大公约数
{
    if(b==0)
        return a;
    else 
        return gcd(b,a%b);
}
fraction reduct(fraction f1)
{
    if(f1.down<0)   		//分母为负,则变为分子为负,分母为正
    {
        f1.up = -f1.up;
        f1.down = -f1.down;
    }
    if(f1.up==0)
        f1.down = 1;
    else
    {	//Abs是对double取绝对值的函数
        long m = gcd(Abs(f1.up), Abs(f1.down));
        f1.up = f1.up/m;
        f1.down = f1.down/m;
    }
    return f1;
}

1035

插入与归并 (25 分)

总结:此题难点在于要能够写出插入排序和归并排序,归并排序可以调用sort函数来编写(时间允许的条件下)。
归并排序:若发现相同数组,则再进行一次排序。

int mergesort(int b[],int c[])
{
	int flag=0;
	for(int i=2;i/2<=n;i*=2)    //i为组内元素个数, i/2为左子区间元素个数
	{
		for(int j=0;j<n;j+=i)   //每i个元素一组,组内[j,min(j+i,n)]排序
		{
			sort(c+j,c+min(j+i,n));
		}
		if(flag==1)
			return 1;
		if(compare(c,b))		//compare函数比较两数组对应位置元素是否相同,若全部相同,则返回true
			flag=1;
	}
	return 0;
}

1036

跟奥巴马一起编程 (15 分)

总结:本题要画一个正方形,先画上边,再画中间的行,最后画底边。上下两边直接for循环打印即可,中间的行数(有字符)=列数的一半(四舍五入)。
四舍五入求行数+打印中间行

    int line;
    if(N%2==1)
        line = N/2+1;
    else
        line = N/2;
    int space_num = N-2;    //中间行的空格
    //print中间行
    for(int i=0; i<line-2; i++)
    {
        printf("%c",c);
        for(int j=0; j<space_num; j++)
            printf(" ");
        printf("%c\n",c);
    }

1037

在霍格沃茨找零钱 (20 分)

总结:两种方法。
1.按总Knut计算,分别计算实付和应付的总Knut并将两者相减,最后再转化为Galleon.Sickle.Knut。思路清晰,代码简单。
2.从低位到高位进行变进制的减法运算。减法涉及借位操作,需特别注意最高位galleon小于-2时需要特殊处理。

    if(knut_pay>=knut_in)
        knut_out = knut_pay-knut_in;
    else    //从sickle_pay借1位
    {
        sickle_pay--;
        knut_out = knut_pay+29-knut_in;
    }
    if(sickle_pay>=sickle_in)
        sickle_out = sickle_pay-sickle_in;
    else    //从sickle_pay借1位
    {
        galleon_pay--;
        sickle_out = sickle_pay+17-sickle_in;
    }
    galleon_out = galleon_pay-galleon_in;
    if(galleon_out<-1)
    {   //转换为-
        galleon_out++;  //galleon_out进一位
        sickle_out = 16-sickle_out;
        knut_out = 29-knut_out;
    }
    printf("%d.%d.%d",galleon_out,sickle_out,knut_out);

注:开始时没有通过测试点4和5,是因为knut没有考虑相等的情况,变为knut_pay>=knut_in后通过。

1038

统计同成绩学生 (20 分)

总结:使用哈希表统计成绩出现的次数:声明一个大小为101的数组记录0~100出现的次数即可。


总结

感想:目前到1038题可以发现PAT对哈希表的考察十分频繁,很多题都需要建一个表之后再查表,一种典型的以空间换时间的操作。开始做PAT时,我用的是DEV-C++,轻量级的IDE很方便,无需建工程就可编译运行,但只能进行打印调试,检查代码逻辑时效率低。之后我换成了VS2010,Debug功能很强,又比VS2017速度快,真是”工欲善其事,必先利其器“。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值