oj第四周作业题解

1148: 【C语言训练】排序问题<1>

题目描述:

将四个整数进行从小到大的顺序排列,

输入:

四个整数

输出:

从小到大输出这四个数


思路:用sort排序(注意,使用sort函数需用头文件algorithm),即需使用数组。

1、定义一个能存4个整数的数组。 

  int a[4];

2、用for循环,循环输入这四个数

for(int i=0;i<4;i++)

    {

        cin>>a[i];

    }

3、用sort函数对这个数组内的四个数进行排列。

sort(a,a+4);

4、循环输出前三个数和空格。

   for(i=0;i<3;i++)

    {

    cout<<a[i]<<"";

}

5、输出第四个数并回车。

  cout<<a[3]<<endl;

 

 

1149: 【C语言训练】排序问题<2>

题目描述:

将十个数进行从大到小的顺序进行排列

输入:

十个整数

输出:

以从大到小的顺序输出这个十个数

思路:从大到小,可用sort函数(注意使用头文件algorithm)从小到大排序后,利用for循环进行逆序输出。

1、定义一个可存10个整数的数组。

  int a[10];

2、利用for循环循环输入这10个整数。

  for(int i=0;i<10;i++)

  {

     cin>>a[i];

  }

3、利用sort函数对着10个数进行从小到大的排序。

sort(a,a+10);

4、 利用for循环逆序输出后9个数和空格。

     for(int i=9;i>0;i--)

  {

     cout<<a[i]<<" ";

  }

5、输出第1个数和回车。

  cout<<a[0];

1155: 【C语言训练】求s=a+aa+aaa+aaaa+aa...a的值

描述

题目描述:

s=a+aa+aaa+aaaa+aa...a的值,其中a是一个一位的整数。  
例如2+22+222+2222+22222(此时共有5个数相加)

输入:

整数a和n(n个数相加,1<= n, a<=9)

输出:

s的值

 

思路:简单题,将一项(例如第4项)分解为2000200 20 2利用嵌套两个循环,外面的循环i从1到n,里面的循环求第i项一共被分解了多少次,再加和,最后求出sum,时间复杂度为O(n^2)

算法优化:其实只用一个循环从1到n,观察每一项,实际上后一项就是前一项*10+a,利用这个递推关系,可以将复杂度降为O(n)

注意这个地方需要一个中间变量

 


 

1412: 工学馆电梯

题目描述:

工学馆电梯比较旧,上升一层需要 6s , 下降一层需要 4s .

现给出一个列表,比如 2 1 , 表示电梯要先从 0 层上升到 2 层,然后在 2 层停住,然后下降到 1 层,在一层停住,结束计算。

电梯每次停的时候会停 5s .

输入:

输入有多行,每一行包含一个 N ,然后是 N 个数(全部小于7).

输出:

计算出电梯一共花费的时间,输出,每行一个。

思路:

  这道题的题意很容易理解,即根据每行输入的数字大小判断电梯是上升还是下降,并依照题目给出的条件来计算整个过程花费的时间。

 

解题算法:

  创建一个整型数组来储存每一行的整数样例,用后一个数与前一个数比较大小来确定电梯是上升还是下降,时间的计算就用两数之差的绝对值乘上对应的时间即可。

 

1406: 奇怪的数列

题目描述:

孙猴子从石头中蹦出来后神通广大,做了花果山的猴王。有一次,一只猴子偷了孙猴子的中餐(就是几个桃子)藏到树洞中,孙猴子大发雷霆,立刻把这只猴子打死了(因剧情需要,其实孙猴子没那么残忍),他来到树洞前,上面有一把密码盘,上面写着:

0 1 0 2 4 7 14 2648 89 ……

输入:

N表示数列的第N项

N<=75注意数据范围

输出:

数列第N项的值

样例输入

5

样例输出

4

 

解题思路:前面一长串剧情。最后给出一个数列,不难发现数列的递推关系为:an=a(n-1)+a(n-2)+a(n-3)+1

 

实现这个数列表达式就需要利用函数和递归的思想,声明一个自定义函数,将函数前三项分别赋值为 0 1 0,后面的所有项就可以通过函数自身的多次调用(递归)求解。

另外注意数据范围,n比较大的时候如果用int就会溢出。

 

  

1432: 土豪学长算成绩

题目描述:

给协会里的同学排成绩对于土豪学长来说总是一个很麻烦的事情,因为土豪学长不会使用excel表格,所以每次他都是手工排序的。现在协会的人数越来越多,手工排序变的不现实了,他希望你能够帮他写一个程序给同学们拍名次。考试的科目有语文,数学,英语,物理,化学,生物。首先按照总分排序,总分一样的按照语文分排序,语文分一样的按照数学排序,以此类推,科目的优先级:语文,数学,英语,物理,化学,生物,如果几门课的分数都一样,那么按照学生的编号排序(序号小的排前面)

输入:

有多组输入数据。每组数据第一行一个数字n,表示学生的个数。接下来n行,每行6个整数,分别对应上面的六门科目。学生按照输入顺序从1n编号。当n0的时候结束。六门科目分数的范围从0100(包含),0<=n<=10000

输出:

学生的id按照排名输出,每组数据的输出占一行。从第一名到最后一名输出。

题目分析:

    这道题有两种变量,一是每个学生的学号,另一个是个人的成绩。题目要求通过比较每个人的总成绩、各科成绩来确定排名,并按照成绩从高到底输出学号。可能刚看到题时会想使用多个数组来进行比较,但考虑后发现,如果总成绩不一样还好,如果每个学生的总成绩一样,那么就要比较单科成绩,而且是多人之间比较,单单是逻辑上就很难完成,所以这道题我们可以考虑使用结构体类型来完成题目。

 

解题算法:

    第一步:定义结构体类型student(或者随便什么都可以),在结构体中声明三个成员:学号,总成绩,各科成绩。其中因为有六科成绩,那么就可以声明一个数组来承接各科成绩。随后用student声明一个数组来承接结构体。

    这里解释一下结构体声明方法:

     struct结构体类型名

     {

          成员表;

}

    结构体类型名数组名[n];(这里声明的数组用来承接成员,数组中的一个元素就是一套成员表,也就是结构体中如果有三个成员,那么数组中的一个元素就包含三个成员。若还是不明白,水平有限,在此道歉,看课本啦~

 

    第二步:定义函数compare(即用来比较学生成绩的函数,名字神马的无所谓),函数中先判断两个(两个,两个)学生的总成绩,在相同的条件下再判断单科成绩,返回值我们可以定位truefalse

 

    第三步:编写主函数,通过循环输入学生信息,并且计算出总成绩。调用函数compare,进行相邻两个学生的成绩的比较,在上面也提到两个,两个~。相邻两个数比较大小,会不会使你想起冒泡排序法,所以在函数中我们返回的是布尔值真假,在真或假的情况下将两个学生的学号对调,进行排序。

 

 

 

 

1302: 【白书习题第二章】韩信点兵

原题

题目描述:

相传韩信机智过人,从不直接清点自己军队的人数。只要让士兵先后以3人一排,5人一排,7人一排的变换队形,而他每次只要看一眼队伍的排位便知道总人数了。输入3个整数abc,表示每种队形队尾人数(a<3b<5c<7)输出总人数的最小值,或者报告无解输出no answer。总人数不超过100,大于10

样例输入

2 1 3

样例输出

no answer

分析

基本方法

利用For循环遍历从11到100的所有数,符合以下条件即可,各条件之间用“且”连接:

·mod 3 == a;

·mod 5 == b;

··mod 7 == c;

(由于从11开始判断,第一个符合条件的数一定是最小值)

没有结果则输出“No answer”。

算法优化

显然,在基本方法中,求余的次数非常多。

用这种方法可以减少求余次数:

假设遍历3的倍数(即 mod 3 ==0),当遇到第一个数12时,接下来便无需遍历13、14,只需直接跳到15,或者说,令这个For循环的步长变为15。

Tip:for(int i = 0 ; i<n ; i+=m ) 中,m即步长,表示计数器i每次循环中增加的量。

算法优化1.1

能想到,如果只能选择一个,在3、5、7中只能选择一个利用上面的方法,肯定选择7,最糟的一种情况是除7余3,程序判断了11、12、13、14、15、16、17、24、31、38、45、52、59、66、73、80、87、94、101一共19个数,比判断3时最小情况之一除三余2——11、14、17、20、23、26、29、32、35、38、41、44、47、50、53、56、59、62、65、68、71、74、77、80、83、86、89、92、95、98、102一共20个数还少。

算法优化1.1.1

既然能对7这么做,其他数也可以。接下来优化5。对7和5的条件都满足后,步长变为7和5的最小公倍数——35。要求除5余0时(最糟情况),不考虑mod 3 的结果,前面1.1的例子就变成了:11、12、13、14、15、16、17、24、31、38、45、80、115——只遍历了13个数。

算法优化1.2

1.1中,11~17这7个数是逐个遍历的。而对于除7余0、1、2、3、4、5、6这七种情况我们完全可以用一个switch解决。(比如要求余3,即原题c==3 时 从17开始遍历

1.1.1中的例子即17、24、31、38、45、80、115,只遍历了7个数。

算法优化1.2.1

很容易想到还可以把1.2的方法扩展到5上,于是我们在上述例子中可以用两个switch从45开始遍历……不过这时候已经写了5*7=35种case了,把所有输入情况3*5*7=105种已经枚举了1/3,显然不需要这么过头。当要求的人数变为10000以上,而又要求输出全部解时,这种方法(甚至你可以把105种全部枚举完)才有一定价值。

 

1307: 【白书习题第二章】子序列的和 题解

原题

题目描述:

输入2个正整数n<m<106,输出1/n2+1/(n+1)2+....+1/m2,保留5位小数.

样例输入

2 4

样例输出

0.42361

分析

基本方法

利用For循环遍历从n到m的所有数,利用累加器记录1/(n^2)的和即可。

常见难排查的错误

输出inf

这表明程序在运行中有数据超限了。

笔者在尝试各种方法时,发现VSC++2017环境下,如果计算1/(n^2)时使用如下代码:

= 1.0 / (n*n);

会发生此错误,原因为:n设置为int,导致n*n返回int类型值时,超出int限制。

解决方法:

使用如下代码:

= 1.0 / (double(n)*n);

或者

= 1.0 / (double(n)* double(n));

想想看,改变了什么?

答案接近,n=1,m=10^6时答案为1.64473

当每一项值都不精确时,答案才会有如此偏离。比如说,用float代替double。

小优化

利用不会再次调用到的输入量作计数器

For循环中,可以把

for (int i=n; i <= m; i++)

改成

for (; n <= m; n++)

减少申请局部变量

如果不需要,关闭流同步

使用std::cin或者std::cout时,默认会与stdin和stdout同步指针,如果不混用两类语句,可以使用

ios::sync_with_stdio(false);

关闭流同步功能。

简单数据不用pow

事实上,pow( , )在小数的效率上还是很低的,

= 1.0 / (double(n)*n);

要比

= 1.0 / pow(n, 2);

快9倍!(*不同配置、不同编译环境有差异)

Tips

如果你的编译器没能帮你统计某一段函数运行时间,你可以自己来。

clock_t starttime, endtime;

  starttime= clock();

{

     //这里填写要测试的语句,可以省略前后的大括号

  }

  endtime= clock();

  cout << endtime - starttime << "/" <<CLOCKS_PER_SEC<<" s "<< endl;

其中,clock_t是一个专门记录clock()返回值的类型。语句大概意思就是记录开始和结束的时间(并不是时间型的数据,也不是真实意义上的时间),然后相减即可。

这里用到了一个常量CLOCKS_PER_SEC,它表示这台电脑一个单位的clock_t是多少分之一秒,一般是1000。

 

1390: 最大公约数和最小公倍数问题

描述

题目描述:

输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数
条件: 1.P,Q是正整数
2.要求P,Q以x0为最大公约数,以y0为最小公倍数.
试求:满足条件的所有可能的两个正整数的个数.

输入:

二个正整数x0,y0

输出:

满足条件的所有可能的两个正整数的个数

样例输入

3 60

样例输出

4

 

思路:

先写一个函数,用辗转相除法求最大公约数或递归的方法求。

主函数中初始化计数字母为0,在x和y的范围内进行P,Q的循环取值,调用前面函数,判断P,Q的最大公约数和最小公倍数是否与x,y相等,若相等,计数字母加一,不想等则继续循环P,Q。最后输出2*计数字母(两个数可以对调)。定义数据时,用long int 因为数据比较大。

 

 

1056: 谭浩强C语言(第三版)习题7.1

描述

题目描述:

用筛法求之N内的素数。

输入:

N

输出:

0N的素数

 

 

 

提供三个思路

1:最容易想到的思路,根据素数的定义,即只能被1和本身整除的数。

先用循环将1n全部录入数组,利用循环的嵌套,从2开始,若a[i]内储存的数可以被除了1和它本身的数外除尽,则将a[i]赋为0;再用一个循环,输出数组中不为0的项,但是复杂度很高

 

2:根据题目要求,筛法,用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛子为空时结束。

long long sushu[n],cnt;

bool prime[MAX]; //布尔 判断变量

void prime()

{

    cnt=1;

    memset(prime,1,sizeof(prime));//先把prime数组全部定义为真

    prime[0]=prime[1]=0;

    for(long long i=2;i<=MAX;i++)

    {

        if(prime[i])

        {

            sushu[cnt]=i;

            cnt++;

        }

        for(long long j=i*2;j<=n;j+=i)//素数的倍数全部不是素数

        {

           prime[j]=0;

        }

    }

}

这种思路是筛法思路,比原来的算法快了很多,但是还是有可以继续优化的地方,10这个数,在2的时候被筛了一次,在5的时候又被筛了一次,出现了重复,当max较大时,重复的次数会很多很多。

3.(2的算法优化)

只筛选 小于等于素数i的素数 与i的乘积,既不会造成重复筛选,又不会遗漏。时间复杂度进一步减小,在此不做具体介绍,有兴趣的可以自行查阅。

 

1320: 分苹果

描述

题目描述:

yyf得到了一批苹果,他可以将其中不超过一半的苹果分给他的一个好朋友,也可以全部留着自己吃。而他的好朋友同样可以将他得到的苹果分给其他的一个人,也可以自己留着。并一直这样分,知道不能再分;求共有多少种分法;例如yyf得到了6个苹果,分法如下:

6

6 3

6 3 1

6 2

6 2 1

6 1;所以共有6种分法

输入:

n表示yyf得到了n个苹果

输出:

分法总数

思路:函数的递归,先将前4个数据输入,后面的数据可以通过递归得到(可以画解答树)

当n为偶数时,yyf第一次分有n/2种分法,接下来继续分,和第一次的分发类似。再用循环求出f(n/2)+……+f(0)的和

当n为奇数的时候,yyf第一次有n-1/2种分法,即f(n)=f(n-1)

 

  

1240: 数列

描述

题目描述:

给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是:  
1,3,4,9,10,12,13,…  
(该序列实际上就是:3^0,3^1,3^0+3^1,3^2,3^0+3^2,3^1+3^2,3^0+3^1+3^2,…)  
请你求出这个序列的第N项的值(用10进制数表示)。  
例如,对于k=3,N=100,正确答案应该是981。 

输入:

只有1行,为2个正整数,用一个空格隔开:  
k N  
(k、N的含义与上述的问题描述一致,且3≤k≤15,10≤N≤1000)。 

输出:

计算结果,是一个正整数(在所有的测试数据中,结果均不超过2.1*10^9)。(整数前不要有空格和其他符号)。

样例输入

3 100

样例输出

981

 

思路:初读这道题看似是一道排序题,其实这是一道进制转换题,因为k^n只有取或者不取两种情况。

3^0对应二进制的1

3^1对应二进制中的10

3^0+3^1对应二进制中的11

………………

而一开始的顾虑会不会有取了一个高次幂但是比很多个低次幂的加和要小的情况,实际上是不存在的(类比二进制)

 

由思想转到代码:首先利用短除法将十进制的n转换位二进制的n,存入数组,然后顺序循环判断数组中的数,为0不处理,为1的话求出pow,加在sum中。

最后输出sum,这题也需要注意数据范围。

猜你喜欢

转载自blog.csdn.net/neuq_zsmj/article/details/78492853