总结一下小数和分数之间精确转换的方法。
首先是分数转换为小数,这个比较简单,先看题 http://acm.hdu.edu.cn/showproblem.php?pid=2522
输入一个n,求1/n的精确表示,如果有循环节只输出最小的一个。
手动模拟一下出发,会发现每次都是上一次除法剩下来的余数*10然后继续下一次计算,什么时候会出现循环呢,显然是余数出现重复的时候。
余数判断重复可以搞一个hash或者set,这里n不是特别大,直接用数组就可以了。
另外写的时候注意一下处理整除的情况。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1e5 + 10; bool vis[maxn]; int main() { int n, T; scanf("%d", &T); while (T--) { scanf("%d", &n); memset(vis, false, sizeof(vis)); int num = abs(n), nowmod = 1, nowval; vis[1] = true; vis[0] = true; if (num == 1) { puts("1"); continue; } if (n < 0) putchar(‘-‘); printf("0."); while (1) { nowval = (nowmod * 10) / num; nowmod = (nowmod * 10) % num; printf("%d", nowval); if (vis[nowmod]) break; vis[nowmod] = true; } putchar(‘\n‘); } return 0; }
另外看一下 http://icpc.ahu.edu.cn/OJ/Problem.aspx?id=441
输入两个数A,B(0<A<=B<=1000),精确输出A/B,并且输出最小循环节长度。
处理方法和上面那个一样,使劲除就好了。 长度的处理就是把原来的vis数组给换成是记录上一次这个数出现的位置即可。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1000 + 10; int pre_pos[maxn]; int main() { int A, B; while (scanf("%d%d", &A, &B), A) { memset(pre_pos, -1, sizeof(pre_pos)); A %= B; if (A == 0) { puts(".\nThis expansion terminates."); continue; } int nowmod = A, nowval, len, nowpos = 0; pre_pos[A] = 0; bool terminal = false; putchar(‘.‘); while (1) { nowpos++; nowval = (nowmod * 10) / B; nowmod = (nowmod * 10) % B; if (nowval == 0 && nowmod == 0) { terminal = true; break; } printf("%d", nowval); if (pre_pos[nowmod] != -1) { len = nowpos - pre_pos[nowmod]; break; } pre_pos[nowmod] = nowpos; } putchar(‘\n‘); if (terminal) puts("This expansion terminates."); else printf("The last %d digits repeat forever.\n", len); } return 0; }
然后是给你小数,指定循环节,让你求分数。 http://icpc.ahu.edu.cn/OJ/Problem.aspx?id=364
这类问题比上面那种要麻烦,需要推一下公式。
给你一个小数 N = 0.A(B),括号内的为循环节,假设A的长度为LenA,B的长度为LenB,有
N * 10^LenA = A.(B) ----> N * 10^LenA - A = 0.(B)
设Y = 0.(B) 有
Y * 10^LenB = B.(B)
Y * 10^LenB - B = 0.(B) = Y
Y = B / (10^LenB - 1) = N * 10^LenA - A
N = (B + A * (10^LenB - 1)) / ( 10^LenA * (10^LenB - 1))
分别计算出分子和分母的值,然后求一下gcd,约分一下就好了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long LL; LL gcd(LL a,LL b) { return b == 0 ? a : gcd(b,a % b); } LL pow10(LL a) { LL ret = 1; while(a--) ret *= 10; return ret; } void display(int a,int b) { int n = 0,m = 0,ta = a,tb = b; while(ta) { n++; ta /= 10; } while(tb) { m++; tb /= 10; } LL denominator,numerator,div; denominator = pow10(n) * (pow10(m) - 1); numerator = a * (pow10(m) - 1) + b; div = gcd(denominator,numerator); cout << numerator / div << "/" << denominator / div << endl; } int main() { int T; cin >>T; while(T--) { int a,b; cin >> a >> b; display(a,b); } return 0; }