题目链接:http://codeup.hustoj.com/problem.php?cid=100000579&pid=2
题目描述
将一个长度最多为30位数字的十进制非负整数转换为二进制数输出。
分析
30位数字的十进制非负整数用C++已有的基本数据类型无法表示
提示:long long 的量级是 1 0 19 ; 10^{19}; 1019;unsigned long long 是 1 0 20 10^{20} 1020,远远不够 1 0 30 10^{30} 1030
于是很自然的想到用高精度除法来做这道题。这里发一张《算法笔记》中高精度除法的知识点讲解图片
高精度数如何定义?上述图片并没有给出,实际上在该书的前几页中已经讲过,这里就不在赘述。
学完高精度除法后,这道题就很好写了,用"高精度除法"+"除基取余"的思路来写。代码如下
提示这里只给出了高精度除法的知识点,如果对高精度不熟悉,可以使用《算法笔记》这本资料学习完整的知识点。这种模板类的知识点一定要多写几遍,孰能生巧,初学一边肯定是记不牢固的。
#include<cstdio>
#include <cstring>
// 定义大整数结构体
struct bign {
int d[40];
int len;
bign() {
memset(d, 0, sizeof(d)); // memset在头文件string.h(cstring)中
len = 0;
} // 构造函数,初始化成员变量
};
// 将字符串转换为大整数
bign strToBign(char str[]) { // 字符串转换为bign
bign a;
a.len = strlen(str); // bign的长度就是字符串的长度
/*倒序存储,原因是,在进行运算的时候都是从整数的低位到高位进行枚举,顺序存储和这种思维
相合,但把整数按字符串%s读入时,实际上是逆位存储的。
即当整数为211时,str[0] = '2',str[1] = '1', str[2] = '1';
因此再读入之后需要在另存为至d[]数组的时候反转一下
*/
for (int i = 0; i < a.len; i++) {
a.d[i] = str[a.len - 1 - i] - '0';
} // 倒序存储
return a;
}
// 高精度除法;被除数a,除数b,余数r
bign divide(bign a, int b, int& r) {
bign c;
c.len = a.len; // 被除数的每一位和商的每一位是一一对应的,因此先令长度相等。
for (int i = a.len - 1; i >= 0; i--) { // 从高位开始
r = r * 10 + a.d[i]; // 和上一位遗留的余数组合
if (r < b) c.d[i] = 0; // 不够除,该位为0
else { // 够除
c.d[i] = r / b; // 商
r = r % b; // 获得本轮的余数
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0) c.len--; // 去除高位的0(大小不变),同时至少保留一位最低位
return c;
}
int main(){
char str1[40]; // 存放高精度数的字符数组
while(scanf("%s", str1) != EOF) { // 多点测试写法
bign a = strToBign(str1); // 转换为高精度数
char ans[150]; // 存放最终结果
int cnt = 0;
do {
int r = 0; // 获得每次的余数
a = divide(a, 2, r); // 做除法
ans[cnt++] = r + '0'; // 形成二进制
} while (!(a.len == 1 && a.d[0] == 0)); // 只要数不为0,就一直除基取余
// 打印结果
for (int i = cnt - 1; i >= 0; i--) {
printf("%c", ans[i]);
}
printf("\n");
}
return 0;
}
参考资料:《算法笔记》