题目:给定一个以十进制表示的分数 a/b,保证 a<b,请将它化成一个二进制的小数。若存在循环节,用圆括号表示循环节。
举个栗子:
in:1 4 out:0.01
in:1.5 out:0.(0011)
还记得十进制转二进制小数部分怎么转吗?对乘二正取整。
就比如举个栗子:0.475 (懂就行,可省略)
第一步:0.475
x 2
0.95----------------------->0
0.95
x 2
1.9------------------------->1
0.9
x 2
1.8------------------------>1
0.8
x 2
1.6------------------------->1
0.6
x 2
1.2------------------------->1
0.2
x 2
0.4------------------------>0
0.4
x 2
0.8----------------------->0
0.8
x 2
1.6----------------->1
0.6。。。。。。。。。
发现没有,0.6在之前出现过了,所以在上次出现的位置开始到1.6的1结束就是循环节,即
0.01111001
但如果像1/3这种十进制就是循环小数怎么办?对,用分数表示
Solution:
每次那个剩余的数
n,m
每次观察有没有一样的(可以用map做,不建议用double,用PII(pair<int,int>)较好)
每次n*=2
如果n>m则n-=m,答案为1
如果n=m则答案为1,直接结束暴力
如果n<m则答案为0
必须要存储答案,用map记录{n,m}时所对应第几次操作,这样可以快速找到括号位置
其他没什么问题,上代码!
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
int n, m, ct, pos = -1;
map<pair<int, int>, int> mp;
//mp[{n,m}]表示n/m在第几次操作,为0表示没有出现过
bool ans[N];//记录答案
void tog(int &n, int &m) {
//全名to-gcd,作用是约分
int t = __gcd(n, m);
n /= t, m /= t;
}
signed main() {
cin >> n >> m;
cout << "0.";//因为n<m,所以0打头
while (1) {
tog(n, m);//先约分,很重要
//如果不约分,1/2和2/4就算两种情况
if (mp[ {n, m}]) {//如果有过
pos = mp[ {n, m}];//记录位置
break;
}
mp[ {n, m}] = ++ct;//记录
n *= 2;//乘二正取整
if (n > m) n -= m, ans[ct] = 1;//n/m>1是
else if (n == m) {//n/m=1时
ans[ct] = 1;
break;
} else ans[ct] = 0;//n/m<1时
}
for (int i = 1; i <= ct; ++i) {
if (pos == i) cout << '(';//如果到位置输出'('
cout << ans[i];
}
if (pos > 0) cout << ')';//是否要输出')'
return 0;
}