咕咕咕、鸽子精又回来了。
今天是我开坑的第六次次发文,大家最近应该都在忙期末把?希望看到的你们可以有时间刷刷题。
今天就要开始解决实际问题了,可能是一个简单的问题从简单开始,一步步走到最后,你我皆超神。如果我有哪些没有讲清楚的,欢迎大家联系我,你提出的问题是我修改完善的基础,万分感谢。
欢迎大家加入我的打卡队列,如果你刷完了对你有帮助请你评论一个打卡。
如果觉得这个文章有用还希望大家交出素质三连呀。
作者简介:一个从工业设计改行学嵌入式的年轻人
✨联系方式:2201891280(QQ)
源码地址:https://gitee.com/xingleigao/algorithm-notes
⏳全文大约阅读时间: 80min
学完了所有的C++入门知识,我们就具备了开始解决问题的能力。需要去习惯不同题型的出题习惯,然后才能不断完善直至超神。今天先看两个比较简单的例子。
模拟就是一类题目怎么说你就怎么做的题目,如果不牵扯到算法就是纯粹的模拟,也叫简单模拟,考察的主要是coding能力。 接下来我们看两道题:
【PAT B1001】 害死人不偿命的(3n+1)猜想 (15 分)
题目描述
卡拉兹(Callatz)猜想:
对任何一个正整数 n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把 (3n+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 n=1。卡拉兹在 1950 年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证 (3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过 1000 的正整数 n,简单地数一下,需要多少步(砍几下)才能得到 n=1?输入格式:
每个测试输入包含 1 个测试用例,即给出正整数 n 的值。
输出格式:
输出从 n 计算到 1 需要的步数。
样例输入
3
样例输出
5
解题思路
读入题目给出的n后,用
while
循环语句反复判断n是否为1
:
- 如果n为1,则退出循环。
- 否则,如果是偶数就令n为n/2;如果是奇数就令n为``(3 * n + 1)/2。并令计数器 step++。
循环结束时就是所需要的答案。
参考代码
#include
int main(){
int n, step = 0;
scanf("%d",&n);
while(n != 1){
if(n %2 == 1) n = (3 * n + 1) / 2;
else n = n/2;
step++;
}
printf("%d\n",step);
return 0;
}
【PAT B1032】 挖掘机技术哪家强
题目描述
为了用事实说明挖掘机技术到底哪家强,PAT 组织了一场挖掘机技能大赛。现请你根据比赛结果统计出技术最强的那个学校。
输入格式:
输入在第 1 行给出不超过 10 5 的正整数 N,即参赛人数。随后 N 行,每行给出一位参赛者的信息和成绩,包括其所代表的学校的编号(从 1 开始连续编号)、及其比赛成绩(百分制),中间以空格分隔。
输出格式:
在一行中给出总得分最高的学校的编号、及其总分,中间以空格分隔。题目保证答案唯一,没有并列。
样例输入
6
3 65
2 80
1 100
2 70
3 40
3 0样例输出
2 150
解题思路
- 令数组
school[maxn]
记录每个学校的总分,初始值为0。对每个读入的学校schID
与其对应的分数score
,令school[schID]+=score。
- 令变量k记录最高总分的学校编号,变量MAX记录最高分,初始值为-1,由于学校的编号是连续的,所以枚举编号1~N-1,不断更新k和MAX就好。
参考代码
#include
const int maxn = 100010;
int school[maxn] = {0};
int main(){
int n, schID, score;
scanf("%d", &n);
for(int i = 0;i < n;i++){
scanf("%d %d", &schID, &score);
school[schID] += score;
}
int MAX = -1, k = 0;
for(int i = 1;i <= n;i++){
if(school[i] > MAX){
MAX = school[i];
k = i;
}
}
printf("%d %d\n", k, MAX);
return 0;
}
有时会需要这样一种情况:
给定一些元素,然后查找某个满足某条件的元素,这是查找操作需要做的事情。查找是学习写代码的一种基本功,是肯定需要掌握的。
一般来说,如果需要在一个比较小范围数据集里进行查找,那么直接遍历每一个元素就好了。如果需要查找的范围很大,就需要使用二分(4.5节)。看一个简单的例子:
问题 B: 找x
题目描述
输入一个数n(1<=n<=200),然后输入n个数值各不相同,再输入一个值x,输出这个值在这个数组中的下标(从0开始,若不在数组中则输出-1)。
输入格式:
测试数据有多组,输入n(1<=n<=200),接着输入n个数,然后输入x。
输出格式:
对于每组输入,请输出结果。
样例输入
4
1 2 3 4
3样例输出
2
解题思路
可以使用一个长度为
n
的数组保存所有元素,然后从0到n-1寻找元素,如果找到就输出对应的小标,找不到就输出-1
。
参考代码
#include
const int maxn = 210;
int a[maxn];
int main(){
int n, x;
while(scanf("%d", &n) != EOF){
for(int i = 0;i < n;i++)
scanf("%d",&a[i]);
scanf("%d",&x);
int k;
for(k = 0;k < n;k++){
if(a[k] == x){
break;
}
}
if(k != n) printf("%d\n",k);
else printf("-1\n");
}
return 0;
}
有些题目会给出一定的规则,需要我们来根据规则画出对应的图形,其实就是由若干字符按照一定的规则输出。一般分为两种形式
- 通过规律直接输出。
- 定义一个二维字符数组,按照规律进行填充一次性输出。
看个例子。
【PAT B1036】 跟奥巴马一起编程 (15 分)
题目描述
美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统。2014 年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个正方形。现在你也跟他一起画吧!
输入格式:
输入在一行中给出正方形边长 N(3≤N≤20)和组成正方形边的某种字符 C,间隔一个空格。
输出格式:
输出由给定字符 C 画出的正方形。但是注意到行间距比列间距大,所以为了让结果看上去更像正方形,我们输出的行数实际上是列数的 50%(四舍五入取整)。
样例输入
10 a
样例输出
aaaaaaaaaa
a a
a a
a a
aaaaaaaaaa
解题思路
由于行数为列数的一半,如果列数是偶数的时候,行数就是
n/2
;如果列数是奇数的时候,行数就是n/2+1
,不如做个统一都是(n+1)/2
。
通过分析发现,由三部分组成,第1行和第row
行都是输出n
个a
,对于第2~row-1的每一行,都是先输出一个a最后再输出一个a,然后中间输出col-2
个a。
所以我们而已先输出一个a,然后输出row-2个空格,然后输出一个a。
参考代码
#include
int main(){
int row, col;
char c;
scanf("%d %c",&col,&c);
row = (col +1)/2; //选取行数
for(int i = 0;i < row;i++){
if(i == 0 || i == row -1){
for(int j = 0;j < col;j++)
printf("%c",c);
puts("");
continue;
}
printf("%c",c);//输出第一个c
for(int j = 0;j < col -2;j++)
printf(" ");
if(col > 1) printf("%c",c);
puts("");
}
return 0;
}
日期的处理总会让人头疼,因为涉及到处理闰年的问题、大月小月的问题,因此细节比较繁杂,但是只要细心处理细节,这类问题就会很简单。
看个例子:
问题 A: 日期差值
题目描述
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。
输入格式:
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
输出格式:
每组数据输出一行,即日期差值
样例输入
20130101
20130105样例输出
5
解题思路
不妨设第一个日期早于第二个日期,如果不早交换就可以了。
有一个常用的思路:令日期不断的加1天,直到第一个日期等于第二个日期即可。
具体操作时:
如果加一天之后天数d等于当前月份m所拥有的天数+1,则令月份m+1,同时置天数为1;
如果月份加1后等于13,则年份加1,同时把月份变为1.
为了方便的计算每个月份的天数,不妨设置一个二维数组int month[13][2]
来表示闰年和平年的每个月份的天数。
注意:如果想要加快速度,就将第一个日期的年份不断加1到与第二个年份相差1即可,期间根据年份来判断加365还是增加366,最后再不断令天数加1操作。
参考代码
#include
int month[13][2] = {{0,0},{31,31},{28,29},{31,31},{30,30},{31,31},{30,30},{31,31},{31,31},{30,30},{31,31},{30,30},{31,31}};
bool isLeap(int year){
//有个口诀,四年一闰。百年不闰,四百年再闰
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int main(){
int time1, y1, m1, d1;
int time2, y2, m2, d2;
while(scanf("%d %d", &time1, &time2) != EOF){
if(time1 > time2){
//swap
time1 = time1 ^ time2;
time2 = time1 ^ time2;
time1 = time1 ^ time2;
}
y1 = time1 / 10000, m1 = time1 % 10000 / 100, d1 = time1 % 100;
y2 = time2 / 10000, m2 = time2 % 10000 / 100, d2 = time2 % 100;
int ans = 1;
//当不相等的时候循环
while(y1 < y2 || m1 < m2 || d1 <d2){
d1++;
if(d1 == month[m1][isLeap(y1)] + 1){
m1++;
d1 = 1;
}
if(m1 == 13){
y1 ++;
m1 = 1;
}
ans++;
}
printf("%d\n",ans);
}
return 0;
}
日常生活使用的进制一般是十进制,而计算机使用的进制是二进制,另外还有八进制,十六进制以及各种数字进制,这就会产生一个问题:对于不同进制如何转换呢?
对于一个P进制转换为Q进制,需要分为两步
- 将P进制转换为十进制
y = a 1 ∗ P n − 1 + a 2 ∗ P n − 2 + . . . + a n − 1 ∗ P + a n y = a_1 * P^{n-1}+a_2 * P^{n-2} + ...+ a_{n-1}*P + a_n y=a1∗Pn−1+a2∗Pn−2+...+an−1∗P+an
这个的相关算法实现也很简单int y = 0, product = 1;//product 不断乘p得到p的不同次方 while(x != 0){ y = y + (x % 10) * product;//每次取出最低位 看起来像十进制 x = x / 10; product *= p; }
- 将十进制转化为Q进制(大名鼎鼎的除留余数法)
例如要将11转化为2进制
11 / 2 商5 余1
5 /2 商2 余1
2/2 商1 余0
1/2 商0 余1
从下往上看 可以得到2进制为(1011)2int z[40], num = 0; do{ z[num++] = y % Q; y /= Q; }while(y != 0);
这里使用do…while的原因是可以规避掉
0
的特殊处理
【PAT B1022】D进制的A+B (20 分)
题目描述
输入两个非负 10 进制整数 A 和 B (≤230−1),输出 A+B 的 D (1
输入格式:
输入在一行中依次给出 3 个整数 A、B 和 D。
输出格式:
输出 A+B 的 D 进制数。
样例输入
123 456 8
样例输出
1103
解题思路
先计算
A+B
的十进制,然后将结果转化为D进制,而十进制转化为D进制的可以使用除留余数法
。
参考代码
#include
int main(){
int a, b, d;
scanf("%d %d %d", &a, &b, &d);
int sum = a + b;
int ans[31], num = 0; //ans存放d进制的没一位
do{
ans[num++] = sum % d;
sum /= d;
}while(sum != 0);
for(int i = num - 1;i >= 0;i--)
printf("%d",ans[i]);
puts("");
return 0;
}
字符串处理是考试中常见的问题,这种题型主要考察的是细心和代码能力,一定要注意边界条件的判断。 这种题最好的方法就是多想,多做,积累经验!!
问题 I: 【字符串】回文串
题目描述
读入一串字符,判断是否是回文串。“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
输入格式:
一行字符串,长度不超过255。
输出格式:
一行字符串,长度不超过255。
样例输入
12321
样例输出
YES
解题思路
假设字符串str的下标从0开始,由于
回文串
正读和反读都一样所以只要遍历字符串的前一半,不需要读到len/2
因为那是正中间的位置,如果全都相等就说明是回文串
,否则说明不是。
参考代码
#include
#include
#include
using namespace std;
const int maxn = 256;
bool judge(char str[]){
int len = strlen(str);
for(int i = 0;i < len / 2;i++)
if(str[i] != str[len - 1 - i]) return false;
return true;
}
int main(){
char str[maxn];
while(cin.getline(str,256)){
bool flag = judge(str);
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
【PAT B1009】1009 说反话 (20 分)
题目描述
给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。
输入格式:
测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区分)组成的字符串,单词之间用 1 个空格分开,输入保证句子末尾没有多余的空格。
输出格式:
每个测试用例的输出占一行,输出倒序后的句子。
样例输入
Hello World Here I Come
样例输出
Come I Here World Hello
解题思路
使用
cin.getline
读入一行,用空格对字符串进行分割,并顺序保存到二维数组中。
注意点:
- 最后如果输出空格会造成错误。
- 由于PAT是单点测试,所以可以使用以下写法
参考代码
#include
int main(){
int num = 0;
char ans[90][90];
while(scanf("%s",ans[num++]) != EOF) num++;
for(int i = num - 1;i >= 0;i--){
printf("%s",ans[i]);
if(i > 0) printf(" ");
}
puts("");
return 0;
}
解题思路2
有些地方可能是多点测试,那么就得老老实实拆分数组了。
参考代码
#include
#include
#include
using namespace std;
int main(){
char str[90];
while(cin.getline(str,90)){
int len = strlen(str), row = 0, col = 0;
char ans[90][90];
//printf("%s %d",str,len);
for(int i = 0;i < len;i++){
if(str[i] != ' ')
ans[row][col++] = str[i];
else{
ans[row][col] = '\0';
row++;
//printf("%d 1",col);
col = 0;
}
}
ans[row][col] = '\0';
for(int i = row; i >= 0; i--){
printf("%s",ans[i]);
if(i > 0) printf(" ");
}
puts("");
}
return 0;
}
今天的题目难度有点,但是是真滴多(量力而行,太多了,每章不带字母的必做,带字母自己选择),我写完会放题解,大家写完了可以在评论区打卡哟!题解我放评论区吧,这样不用修改文章。
对应章节 | 题目 | 相同链接 |
---|---|---|
简单模拟 | 3.1小节——入门模拟->简单模拟 | |
简单模拟 | B1001 害死人不偿命的(3n+1)猜想 (15 分) | |
简单模拟 | B1011 A+B 和 C (15 分) | |
简单模拟 | B1016 部分A+B (15 分) | |
简单模拟 | B1026 程序运行时间 (15 分) | |
简单模拟 | B1046 划拳 (15 分) | |
简单模拟 | B1008 数组元素循环右移问题 (20 分) | |
简单模拟 | B1012 数字分类 (20 分) | |
简单模拟 | B1018 锤子剪刀布 (20 分) | |
简单模拟 | A1042 Shuffling Machine (20 分) | |
简单模拟 | A1046 Shortest Distance (20 分) | |
简单模拟 | A1065 A+B and C (64bit) (20 分) | |
简单模拟 | B1010 一元多项式求导 (25 分) | |
简单模拟 | A1002 A+B for Polynomials (25 分) | |
简单模拟 | A1009 Product of Polynomials (25 分) | |
查找元素 | 3.2小节——入门模拟->查找元素 | |
查找元素 | B1041 考试座位号 (15 分) | |
查找元素 | B1004 成绩排名 (20 分) | |
查找元素 | B1028 人口普查 (20 分) | |
查找元素 | B1032 挖掘机技术哪家强 (20 分) | |
查找元素 | A1006 Sign In and Sign Out (25 分) | |
查找元素 | A1011 World Cup Betting (20 分) | |
查找元素 | A1036 Boys vs Girls (25 分) | |
图形输出 | 3.3小节——入门模拟->图形输出 | |
图形输出 | B1036 跟奥巴马一起编程 (15 分) | |
图形输出 | B1027 打印沙漏 (20 分) | |
图形输出 | A1031 Hello World for U (20 分) | |
日期处理 | 3.4小节——入门模拟->日期处理 | |
进制转换 | 3.5小节——入门模拟->进制转换 | |
进制转换 | B1022 D进制的A+B (20 分) | |
进制转换 | B1037 在霍格沃茨找零钱 (20 分) | |
进制转换 | A1019 General Palindromic Number (20 分) | |
进制转换 | A1027 Colors in Mars (20 分) | |
进制转换 | A1058 A+B in Hogwarts (20 分) | |
字符串处理 | 3.6小节——入门模拟->字符串处理 | |
字符串处理 | B1006 换个格式输出整数 (15 分) | |
字符串处理 | B1021 个位数统计 (15 分) | |
字符串处理 | B1031 查验身份证 (15 分) | |
字符串处理 | B1002 写出这个数 (20 分) | |
字符串处理 | B1009 说反话 (20 分) | |
字符串处理 | B1014 福尔摩斯的约会 (20 分) | A1061 Dating (20 分) |
字符串处理 | B1024 科学计数法 (20 分) | A1073 Scientific Notation (20 分) |
字符串处理 | B1048 数字加密 (20 分) | |
字符串处理 | A1001 A+B Format (20 分) | |
字符串处理 | A1005 Spell It Right (20 分) | |
字符串处理 | A1035 Password (20 分) | |
字符串处理 | A1077 Kuchiguse (20 分) | |
字符串处理 | A1082 Read Number in Chinese (25 分) |
题解:评论区见,置顶没有就是我没写完0.0,大佬们刷完打个卡
大家加油冲!!!!