练习作业5: 韩信点兵 , 统计方案 , 出现最频的数
前排提示:百度搜题搜的爽,一直搜题一直爽
当然搜题就是交 就等着被放生吧88(狗头
本次题目理论上都应该可以独立完成?
问题 A: 韩信点兵
序号:1055
题目描述
淮安民间传说着一则故事——“韩信点兵”,其次有成语“韩信点兵,多多益善”。韩信带1500名兵士打仗,战死四五百人,站3人一排,多出2人;站5人一排,多出4人;站7人一排,多出6人。韩信马上说出人数:1049。如果我给你一些条件,你能给我满足条件的最小正整数么?
当然,这道题是简单的。给你的每组数据有三行数字,每行数字的第一个数是除数 d ,第二个数是余数 m,然后请你求出一个最小的正整数 n(n < 1000,000),此n能使这三行数字同时满足 n/d == x……m.
输入
第一行是数据的组数 nCase (n<=5),以下有3nCase行。每三行是一组数据,就是给你的三对数据。
输出
对于每组输入,输出一行,是所求得的最小正整数。
样例输入
2
3 2
5 4
7 6
9 2
7 3
6 5
样例输出
104
101
思路过程:
? 看到韩信点兵,那当然是多多益善啦。那到底多不多呢,我们来看看。首先理解题目意思,第一个输入是nCase,就是数据的组数,和前几期一样。然后是3nCase行,意思就是说每三个是一组的。我们再来看输入的数据,每组数据第一个是除数d,第二个数是余数m。
输入:
组数nCase
除数d1 余数m1
除数d2 余数m2
除数d3 余数m3* nCase
? 读完后理解大概意思就是给出三组数,求出满足的最小数字n,使得n/d == i 余 m,(类似于最小公倍数?)判断条件就是if(n%d==m)
。
? 先搭出框架,一个while循环,由输入的nCase决定做几次。(OJ提示,可以做一次输出一次,不算错误,不用最后一起输出)
while(nCase--)
{//循环体}//这是做多少次的标准形式,因为输入nCase之后每做一次直减,最后做了n次就不做了
? 然后我们定义除数和余数,由于每组三个,我们可以就定义六个数反复使用,由于是余数,这里用 % 。然后进行循环求解n,在这里我首先用了for循环,因为题目规定n<1000,000,所以我设n从1-1000,000for循环,满足三组条件即break跳出,最后输出n。
for(n=0;n<1000000;n++)if(n%d1==m1&&n%d2==m2&&n%d3==m3)break;
? 我们也可以使用while循环,n从1自增再进行判断,满足条件的第一个n就直接跳出输出。
while(++n)//不标准{if(n%d1==m1&&n%d2==m2&&n%d3==m3){ printf("%d\n",n);break;}}
? 这样就完成了
Example Answer:
#include<stdio.h>
int main()
{int nCase,n,d1,d2,d3,m1,m2,m3;scanf("%d",&nCase);while(nCase--){n=0;//n重置为0scanf("%d %d",&d1,&m1);//条件组1scanf("%d %d",&d2,&m2);//组2scanf("%d %d",&d3,&m3);//组3while(++n)//这里写的并不标准,判断体里应为判断条件。//可是这里没有判断条件,直到找到n为止,所以就把n自增写到了里面 {if(n%d1==m1&&n%d2==m2&&n%d3==m3)//可以用一个if直接判断完毕 break;//条件成立即n现在就为答案 }printf("%d\n",n);}return 0;
}
问题 B: 统计方案
序号:1068
题目描述
在一无限大的二维平面中,我们做如下假设:
1、每次只能移动一格;
2、不能向后走(假设你的目的地是“向上”,那么你可以向左走,可以向右走,也可以向上走,但是不可以向下走);
3、走过的格子立即塌陷无法再走第二次。
求走n步不同的方案数(2种走法只要有一步不一样,即被认为是不同的方案)。
输入
首先给出一个正整数C,表示有C组测试数据。
接下来的C行,每行包含一个整数n(n<=20),表示要走n步。
输出
请编程输出走n步的不同方案总数;
每组的输出占一行。
样例输入
2
1
2
样例输出
3
7
思路过程:
题目需要理解一下,首先输入正整数C,表示C组测试数据,这个就和上一题nCase一样,套用标准模板。然后输入n(n<=20),一行一组,表示走n步。这样大概知道了输入格式,然后我们看题目描述,走的n步有要求,1.每次一格,2.不能后退,3.不能返回。
乍一看很复杂的题目,实际上它就类似于数学题找规律,没有别的办法,我们先枚举几个看看。
首先如果n==1,有三种方案
左;
中;
上。
如果n==2,那就有7种方案
左左,左上;
上左,上上,上右;
右上,右右。
如果n==3,那就有17种方案
左左左,左左上,左上左,左上上,左上右;
上左左,上左上,上上左,上上上,上上右,上右上,上右右;
右上左,右上上,右上右,右右上,右右右。
如果n==4,那就有41种方案
左左左左,左左左上,左左上左,左左上上,左左上右,左上左左,左上左上,左上上左,左上上上,左上上右,左上右上,左上右右;
上左左左,上左左上,上左上左,上左上上,上左上右,上上上左,上上上上,上上上右,上上右上,上上右右,上右上左,上右上上,上右上右,上右右上,上右右右;
右上左左,右上左上,右上上左,右上上上,右上上右,右上右上,右上右右,右右上左,右右上上,右右上右,右右右上,右右右右;
(写得我脑壳疼…)
我们可以猜测,n==5有99种方案
看出来这是一种类似于向上延伸树枝的结构(可惜我不想画图)
每往左或右就多出两种方案,每往上就多出三种方案
总结规律,看整体,或者就看其中一条分支,我们能知道从第三项开始,每一项都是前一项的两倍加上往前第二项的和。
由于n<=20,我们可以把前20种方案数编入数组,先进行运算,最后调出数组元素就好了。
f[0] = 3;f[1] = 7;for(i=2;i<20;i++)f[i]=2*f[i-1]+f[i-2];
Example Answer:
#include<stdio.h>
int main()
{int a[20]={3,7};//定义同时为数组开始两位赋值int i,C,n;scanf("%d",&C);//输入for(i=2;i<20;i++)a[i]=2*a[i-1]+a[i-2];//循环求出前20种方案数并存入数组while(C--)//标准循环次数格式{scanf("%d",&n);printf("%d\n",a[n-1]);//注意是第n-1个元素不是第n个}return 0;
}
问题 C: 出现最频的数
序号:1090
题目描述
读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20。然后程序将对这些整数进行统计,把出现次数最多的那个整数值打印出来。如果有两个整数出现的次数相同,即并列第一,那么只打印比较小的那个值。
输入
每行一个整数。第一个整数为N(0<N<20),后面跟N个整数,N个整数从小到大排列。
输出
一个整数。
样例输入
5
100
150
150
250
250
样例输出
150
思路过程:
先看输入格式,先输一个N(0<N<20)表示输入数的个数,后面是输入的数。看题目要求,打印出现次数最多的数,并列最多打印较小的数。
我首先的想法是定义大小为N的数组a[N]
,再定义大小为N*2的二维数组b[N][2]
,第一列存数,第二列存出现的次数。
scanf("%d",&n);int a[n];int b[n][2];for(i=0;i<n;i++)scanf("%d",&a[i]);
写for循环,嵌套for循环,如果a[i]
与b[N][0]
中的一个值相等,那么b[N][1]
自增,如果不相等,说明这时a[i]
是第一次出现,定义k=0表示b存了多少不相等的数,把a[i]
存入b[k][0]
,然后b[k][1]
自增,k自增。
for(i=0;i<n;i++){for(j=0;j<n;j++){if(a[i]==b[j][0])break;}if(j==n){b[k][0]=a[i];b[k++][1]++;}else b[j][1]++;}
最后比较,max为出现次数最多的数的下标,比较b[max][1]
与b[i][1]
,最后输出b[max][0]
再次注意,第一列为数,第二列为出现次数。
for(i=0;i<k;i++)if(b[max][1]<b[i][1]&&i>max)max=i;printf("%d",b[max][0]);
虽然有点麻烦,但这样的算法是可行的,但OJ机不给我过??本地跑的数据是对的,测试机跑的数据居然和本地跑出来的数据不一样?而且很明显机子的计算竟然是错的!
无语,换方法吧。
再读题,发现忽略一条,输入的数据是从小到大排列好的!这是非常重要的一点,这样一来,我们只要顺着数据一路爬下去,记录最大的出现数和下标,再对比最后输出就好啦。
如果第i项与i-1项相等,那么次数自增,如果不相等,那么立刻与当前记录的!出现最多的次数比较,如果大于,那么就把本项下标替换,把最大次数替换,再重置次数继续循环,如果不大于,那么就直接重置次数继续循环。
这里用多个else就是这个原因
我们比较是从小到大比,由于数列是排序好的,我们不用担心有出现相同次数的数要输出比较小的那个,因为我们记录到的一定是最小的,当比较到出现次数一样大但比较大的数时,次数 并不大于 最大次数,不会替换掉,所以不用担心。
本题就这样解决啦
for(i=1;i<n;i++){if(a[i]==a[i-1])time++;//次数自增else if(time>maxtime)//一不同就和最大出现次数比较{ maxtime=time;time=1;max=i-1;}else time=1;//那么次数time重置为1}
理论上这种方法只能用于数据组是排列好的,而第一种可以用于任何数据组(但机子有问题我咋办…)
Example Answer:
#include <stdio.h>
int main()
{ int n,i;int max=0;//下标int maxtime=1,time=1; //定义出现次数scanf("%d",&n); //数据组数int a[n];for(i=0;i<n;i++)scanf("%d",&a[i]);//为数组赋值for(i=1;i<n;i++){if(a[i]==a[i-1])time++;//如果下一个数据和前一个相同,那么次数自增else if(time>maxtime)//一不同了,就和最大出现次数比较{ maxtime=time;time=1;max=i-1;}else time=1;//这里用了多个else,这里是跟随上面的,如果出现次数小于最大出现次数,那么次数time重置为1}printf("%d\n",a[max]);//最后输出return 0;
}
小结:
这几次作业的题目真是越来越简单了,而且知识范围也在已学之内,这次是本学期最后一次OJ作业了(吐槽:上届学长和上上届学长也来凑热闹了…),希望同学能提升自己的算法能力,现在更加考验思维而不是实现了。写出来博客也是为了记录我做题的思路,也为以后我再看这题嘲笑我自己以前算法思维这么差埋下了伏笔233.
还有就是抄百度答案的问题,抄倒是改改啊,后台一查都是一样的不尴尬嘛…emmm还是要说,学习是自己的事,尽量不要直接抄,看好人家的解析,理解算法的意思,才能在算法这条路上越走越远!
提前祝大家新年快乐啦!!
如果有问题或者错误请见谅并联系我改正!一起努力学习!