重点
一. 如何判断出现循环小数
我冥思苦想几天,终于自己独立想出个简单办法。我用自己的 三星 Note9 Pen在手机上演算的。(图为当时草稿可能有错,以文章文字为主。)
其实这些算术题目,最好用简单案例推导。以前看公开课,老外上课,都是讲的特别简单,但是每个重点都会用例子讲解,并且例子讲解的超级详细,听我的昏昏欲睡。后来才觉得仔细研究例子才是最快的办法,比干用脑袋有效。(因为这不是数学证明题,无需严谨的推导,能依葫芦画瓢即可,凭直觉猜出逻辑即可)
说正题 ,如 76/25。 图中 S 是 存放 商 的数组, Y 是存放 余数的数组。以下对照图片看更有效。
1. 首先,被除数 是76, 除数 是25。
2. 第一个商 是 3 ,余数 是 1,都存到数组里,后面要用。
2. 余数1*10 = 10,作为新的被除数。除数永远不变。
3. 第二个商 是0 ,余数是 10,保存待用。
4. 余数10*10= 100,作为新的 被除数。除数不变。
5. 第三个商是4,余数是 0,保存待用。
6. 余数0*10=0, 作为新被除数
7. 第四个商是0,余数是0,保存待用。
8. 发现第四个余数,和第三个余数一样的。余数一样意味什么呢,意味着下一步的被除数都是一样的,除数又永远不变,下一步的商 就也是一样的。之后陷入无限的一样,陷入循环。
然后,这时就可以确定:第四个商就是最小的循环体。
推广一下,随便举个例子。
i = 0,1,2,3,4,5
S=3,0,4,5,6,7
|-------|
Y=1,10,8,9,2,8
|----------|
当 i = 5(第六个余数)时,和 i = 2(第三个余数)余数相同,则说明后面就可以陷入循环了,后面的商肯定是 567567...(可以自己演算下为什么是 567 ,而不是 4567)。
则此时,循环部分的起始位就是 i=3,结束位是 i=5。
二、代码
将上面的伪代码写成C或者C++即可。
第一次,Y 数组和 S 数组设置为200大小,被爆 Runtime Error,后改为3000,AC通过。但从题目我看不出来,具体需要多大。
题目要点
1. EOF作为输入结束标志
scanf() 和 getchar() 都可以捕获 EOF ,EOF其实就是被define为-1的无符号整数。
2. 输出最短的循环小数
0.0333... 循环部分就是 3 ,从第二个小数位开始,长度是1
0.2500...循环部分是0,从第三个小数位开始,长度是0
5/43 = 0.(116279069767441860465) 循环部分是116...,从第一个小数位开始,长度是211/397 = 0.(00251889168765743073047858942065491183879093198992...)循环部分是0025...992...,从第一个小数位开始,长度是99
3. 输出格式
循环部分小于50位的,直接输出;多于50位的,只输出前面50位。前后括号和省略号不算在50位里面。
1/397 = 0.(00251889168765743073047858942065491183879093198992...) 99 = number of digits in repeating cycle
%d/%d(空格1个)=(空格1个)%d.(%d...)
(空格3个)%d(空格1个)=(空格1个)number of....
(\n换行)
AC代码
我不保证是最优的代码,但是可以AC。我按照黑书(算法设计竞赛第二版)做的,虽然C/C++/JAVA也熟,但还是按照书本顺序,只用C语言实现,所以看着有些臃肿。
//3-8.c UVa202,2019-03-23
//余数一旦出现过,循环就开始了
#include <stdio.h>
#include <string.h>
#include <math.h>
//#define LOCAL
int Y_arr[3000];
int S_arr[3000];
int ifRepeat(int *arr, int len , int a ){
//只需和之前的比较,len-1 是当前元素 a
for (int i = 0; i < len - 1; ++i)
{
if (arr[i] == a)
{
return i;
}
}
return -1;
}
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout)
#endif
int a = 0, b= 0;
int i =0 ,beichushu = 0, chushu = 0;
int s = 0, y = 0; //商 ,余数
int cycleBgnInx = 0, cycleEndInx = 0,cycleLen = 0; //循环开始,循环结束
while(scanf("%d %d",&a, &b)!=EOF){
memset(Y_arr, 0, sizeof(Y_arr));
memset(S_arr, 0, sizeof(S_arr));
beichushu = a;
chushu = b;
for (i = 0; ; ++i)
{
s = beichushu / chushu;
y = beichushu % chushu;
S_arr[i] = s;
Y_arr[i] = y;
//余数若出现过,则后面的计算是重复值
if(ifRepeat(Y_arr, i+1 ,y) != -1)
{
cycleBgnInx = ifRepeat(Y_arr, i+1 ,y)+1;
cycleEndInx = i ;
cycleLen = cycleEndInx - cycleBgnInx + 1;
break;
}else{
beichushu = y * 10;
}
}
//allshang[0-recycleEndInx] is result
//length is recycleEndInx+1
// printf("lenth is: %d\n", recycleEndInx);
printf("%d/%d = %d.", a, b, S_arr[0]);
//output not cycle part
for (i = 1; i < cycleBgnInx ; ++i)
{
printf("%d", S_arr[i]);
}
printf("(");
//output cycle part
if ( cycleLen <= 50)
{
for (i = cycleBgnInx; i <= cycleEndInx ; ++i)
{
printf("%d", S_arr[i]);
}
}else{
for (i = cycleBgnInx; i < 50+cycleBgnInx ; ++i)
{
printf("%d", S_arr[i]);
}
printf("...");
}
printf(")\n");
printf(" %d = number of digits in repeating cycle\n", cycleLen);
printf("\n");
}
return 0;
}