习题 3-8 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)
题目描述:
输入整数a和b (0<=a<=3000, 1<=b<=3000),输出a/b的循环小数表示以及循环节长度。例如a=5, b=43,小数表示为0.(116279069767441860465),循环节长度为21
第一想法就是字符串匹配,虽然走了弯路,好在最后AC了
#include<stdio.h>
#include<ctype.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
const int MAX_N = 10000;
int a, b;
int resInt; // 存储结果的整数部分
char res[MAX_N]; // 存储结果的小数部分
char cycle[2*MAX_N]; // 存储循环节
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
while(scanf("%d%d", &a, &b) == 2) { // 当成功读取到a和b
memset(res, '0', sizeof(res)); // 重置结果和循环节
memset(cycle, '\0', sizeof(cycle));
// 计算整数部分
int numA = a, numB = b;
resInt = a/b;
a = (a - resInt * b) * 10;
int resIndex = 0;
// 计算小数部分
while(resIndex < MAX_N - 1) {
if(a < 1){
break;
}
while(a < b) {
a = a * 10;
resIndex++;
}
int num = a/b;
res[resIndex] = (char)('0' + num);
a = a - num*b;
}
int lenCycle = 1; // 循环节长度
int site = 0; // 循环节位置,从小数点后开始数
bool found = false;
for(int i = 0; i < MAX_N; i++) { // 寻找循环节位置
int cTime = 0; // 循环次数
for(int j = 1; j <= MAX_N/2; j++) { //寻找循环节长度
cTime = 0;
memset(cycle, '\0', sizeof(cycle));
strncpy(cycle, res + i, j);
for(int k = 0; i + (k+1) * j < MAX_N; k++) { // 判断是否构成循环节
if(strstr(res + i + k*j, cycle) == res + i + k*j) {
cTime++;
found = true;
} else {
found = false;
break;
}
}
if(found && cTime > 1) {
lenCycle = j;
site = i;
break;
}
}
if(found && cTime > 1) break;
}
printf("%d/%d = %d", numA, numB, resInt);
printf(".");
for(int i = 0; i < site; i++) {
printf("%c", res[i]);
}
printf("(");
for(int i = 0; i < lenCycle && site + i < 50; i++) printf("%c", cycle[i]);
if(site + lenCycle > 50) printf("...");
printf(")\n");
printf(" %d = number of digits in repeating cycle\n\n", lenCycle);
}
return 0;
}
下面是判断被除数是否相同的方法,代码相对简洁一些
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAX_N = 100000;
int mod[MAX_N]; // 存储被除数
int ans[MAX_N]; // 存储所求小数
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
int a, b;
while(scanf("%d%d", &a, &b) == 2) {
memset(mod, 0, sizeof(mod));
memset(ans, 0, sizeof(ans));
ans[0] = a/b;
printf("%d/%d = %d.", a, b, ans[0]); // 输出整数部分
int pos = 1; // 小数位数 也是循环体结束位置
int start = 1; //循环体开始位置,小数点后第几位
int count = 0; // 被除数的个数 (循环体之前的小数位数)
a = (a % b) * 10;
while(!(start = mod[a])) { // 被除数没重复
// 计算下一个被除数和小数下一位
mod[a] = ++count; // 存放第count个被除数a
ans[pos++] = a / b;
a = (a % b) * 10;
}
for(int i = 1; i < start; i++) {
printf("%d", ans[i]);
}
printf("(");
for(int i = start; i < pos && i < 51; i++) {
printf("%d", ans[i]);
}
if(pos > 50) printf("...");
printf(")\n");
printf(" %d = number of digits in repeating cycle\n\n", pos - start);
}
return 0;
}