文章目录
前言:对整数进制转换的理解
1.1进制理解
数制:也称为计数制,是一种计数的方法,是用一组固定的符号和统一的规则来表示数值的方法。在计数过程中采用进位的方法称为进位计数制(进制),包括数位、基数和位权三个要素。
数位
:指数字符号在一个数中所处的位置。
基数
:指在某种进位计数制中数位上所能使用的数字符号的个数。例如十进制的基数为10。
位权
:数制中某一位上的1所表示数值的大小(所处位置的价值)。例如十进制的231,2的位权是100,3的位权是10,1的位权是1。
以2进制下的10011111
为例:
首先10011111
为二进制数,它的基数是2。那么计算位权的时候位权的底数是2。
数字 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
---|---|---|---|---|---|---|---|---|
数位 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
位权 | 2 7 2^{7} 27 | 2 6 2^{6} 26 | 2 5 2^{5} 25 | 2 4 2^{4} 24 | 2 3 2^{3} 23 | 2 2 2^{2} 22 | 2 1 2^{1} 21 | 2 0 2^{0} 20 |
1.2如何转换?
1.2.1通法:
将b进制下的数转换为十进制下的数,再转换为目标进制下的数。
例如:
10011111
的十进制表示是:
1
∗
2
7
+
0
∗
2
6
+
0
∗
2
5
+
1
∗
2
4
+
1
∗
2
3
+
1
∗
2
2
+
1
∗
2
1
+
1
∗
2
0
=
159
1*2^{7}+0*2^{6}+0*2^{5}+1*2^{4}+1*2^{3}+1*2^{2}+1*2^{1}+1*2^{0}=159
1∗27+0∗26+0∗25+1∗24+1∗23+1∗22+1∗21+1∗20=159
再将其转换为7进制下的数:
步骤 | 被除数 | 商 | 余数 |
---|---|---|---|
1 | 159 | 22 | 5 |
2 | 22 | 3 | 1 |
3 | 3 | 0 | 3 |
注意商和被除数的更迭。
将余数反序记录为:315
即为所求7进制下的表示。
1.2.1特别转换
二进制、八进制、十六进制由于
2
3
=
8
2^{3}=8
23=8和
2
4
=
16
2^{4}=16
24=16的特殊关系,可以将二进制下的数从末位按三位一组(八进制)或四位一组(十六进制)直接进行转换对应的数字符。对应关系可参考下表:
二进制 | 八进制 | 十六进制 | 十进制 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
10 | 2 | 2 | 2 |
11 | 3 | 3 | 3 |
100 | 4 | 4 | 4 |
101 | 5 | 5 | 5 |
110 | 6 | 6 | 6 |
111 | 7 | 7 | 7 |
1000 | / | 8 | 8 |
1001 | / | 9 | 9 |
1010 | / | A | 10 |
1011 | / | B | 11 |
1100 | / | C | 12 |
1101 | / | D | 13 |
1110 | / | E | 14 |
1111 | / | F | 15 |
问题 A: 5201 二进制位
题目描述
给定你一个十进制数n(0 < n < 1000),要求输出其二进制数。
输入
输入有多行,每行包括一个十进制的正整数n。
输出
输出对应的二进制数。
样例输入
1
2
3
样例输出
1
10
11
解析
思路梳理
读入数据后记录每次对2取的余数,并反序输出。
细节提示
reverse()函数可以用于
- 翻转数组:
reverse(a,a+n);
- 翻转字符串:
reverse(str.begin(),str.end());
- 翻转向量:
reverse(vec.begin(), vec.end());
参考代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
while(cin>>n){
string s0;
while(n>1){
s0 += to_string(n % 2);
n /= 2;
}
reverse(s0.begin(), s0.end());
cout << n << s0 << endl;
}
return 0;
}
问题 B: 5202 二进制转化为十六进制
题目描述
输入一个2进制的数,要求输出该2进制数的16进制表示。在16进制的表示中,A-F表示10-15。
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个以0和1组成的字符串,字符串长度至少是1,至多是10000。
输出
n行,每行输出对应一个输入。
样例输入
2
100000
111
样例输出
20
7
解析
思路梳理
2进制转16进制可以以四位为一组进行转换。
细节提示
考虑到二进制数的长度至多有1000位,我们需要以字符串形式输入。且以四位为一组时应该从最右位开始分组。
参考代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
while(n--){
string num, s0, res;
cin >> num;
while(num.length()>=4){
s0 = num.substr(num.length() - 4, 4);
num = num.substr(0, num.length() - 4);
int nx = (s0[0] - '0') * 8 + (s0[1] - '0') * 4 + (s0[2] - '0') * 2 + (s0[3] - '0');
if(nx<10)
res += to_string(nx);
else
res.push_back(char(nx - 10 + 'A'));
}
reverse(res.begin(), res.end());
if(num.length()==0)
cout << res;
else if (num.length() == 1)
cout << num << res;
else if(num.length() == 2)
cout << (num[0] - '0') * 2 + (num[1] - '0') << res;
else if(num.length()==3)
cout << (num[0] - '0') * 4 + (num[1] - '0')*2+(num[2]-'0') << res;
cout << endl;
}
return 0;
}
问题 C: 5203 周易II
题目描述
有人说,中国古代的“周易”是二进制系统的起源,在该系统中,他们用“- -”表示1,“—”表示0。因此,二进制数字“011010”可以表述为“—\n- -\n- -\n—\n- -\n—\n”(符号“\n”表示换行)。现在的问题是如何把一个“周易”中的二进制转换为相应的十进制数?
输入
文件中包含多组测试数据。每个测试数据都是以一个数字k (0 < k <= 20)开始,表示这个数在二进制中的位数。接下来就是k行,就是字符串—或- -,第1位表示最高位。
输出
对于每组测试数据,输出对应的十进制数。
样例输入
3
- -
- -
- -
3
---
---
---
6
---
- -
- -
---
- -
---
样例输出
7
0
26
解析
思路梳理
这题比较简单,思路和问题A差不多,比问题A还简单一些。
细节提示
实际进行数据测试时,如果直接使用st.compare("---")==0
来判断字符会无法通过,可能是测试点中并不是每行都是---
或者- -
,但是前三个字符一定是符合的,所以建议使用st[0]=='-'&&st[1]=='-'&&st[2]=='-'
判断。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 30;
int nx[N];
int main(){
int n;
while(scanf("%d ",&n)!=EOF){//多扫描一个空格避免换行存在
int i = 0;
while(n--){
string st;
getline(cin, st);
if (st[0]=='-'&&st[1]=='-'&&st[2]=='-')
nx[i++] = 0;
else if(st[0]=='-'&&st[1]==' '&&st[2]=='-')
nx[i++] = 1;
}
if(i>=1){
long long ind = 1, res = 0;
for (i-=1; i >= 0;i--){
res += ind * nx[i];
ind *= 2;
}
cout << res << endl;
}
else if(i==0)
cout << 0 << endl;
}
return 0;
}
问题 D: 5204 二进制数
题目描述
给定一个正整数n,要求输出对应的二进制数中所有数码“1”的位置。注意最低位为第0位。例如13的二进制形式为1101,因此数码1的位置为:0,2,3。
输入
输入文件中的第1行为一个正整数d,表示输入文件中测试数据的个数,1<=d<=10,接下来有d个测试数据。每个测试数据占一行,只有一个整数n,1<=n<= 106。
输出
输出包括d行,即对输入文件中的每个测试数据,输出一行。第i行,1 <= i <= d,以升序的顺序输出第i个测试数据中的整数的二进制形式中所有数码1的位置,位置之间有1个空格,最后一个位置后面没有空格。
样例输入
2
13
127
样例输出
0 2 3
0 1 2 3 4 5 6
解析
思路梳理
先用n%2判断二进制位上是否是1,若是则输出此时的i值,i表示数位,每次进行n/=2后均增加1。
细节提示
注意最后一个数没有空格。
参考代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int d;
cin >> d;
while(d--){
int n;
cin >> n;
int i = 0;
while(n>0){
int flag = 0;
if(n%2==1){
cout << i;
flag = 1;
}
n /= 2;
i++;
if(n!=0&&flag)
cout << " ";
}
cout << endl;
}
return 0;
}
问题 E: 5205 留下最少
题目描述
给定一个基数b,和b进制下的两个非负整数p和m,计算p%m,并输出b进制下的结果。p%m的结果被定义为非负整数k,要求k是满足p=a * m + k中的最小值(a为某些整数)。
输入
输入包含多组测试数据。每个测试数据占一行,由3个无符号的整数构成,第一个整数为b,一个介于2到10之间的十进制数。第二个为p,可能由1000位的0到b-1构成。第三位是m,由9位0到b-1构成。
最后一个测试数据为0,表示输入结束。
输出
对应每组测试数据,要求输出一行b进制下的p%m。
样例输入
2 1100 101
10 123456789123456789123456789 1000
0
样例输出
10
789
解析
思路梳理
由描述易知,m的值较小,可以用整型储存,所以可以直接定义一个长整型mx来作为m的数值。
利用定理:
(
a
+
b
)
m
o
d
c
=
(
a
m
o
d
c
+
b
m
o
d
c
)
m
o
d
c
(
a
p
)
m
o
d
c
=
(
(
a
m
o
d
c
)
p
)
m
o
d
c
(
a
∗
b
)
m
o
d
c
=
(
a
m
o
d
c
∗
b
m
o
d
c
)
m
o
d
c
(a+b)modc=(amodc+bmodc)modc\\ (a^p)mod c=((a mod c)^p)mod c\\ (a*b)mod c=(amodc*bmodc)mod c
(a+b)modc=(amodc+bmodc)modc(ap)modc=((amodc)p)modc(a∗b)modc=(amodc∗bmodc)modc
假如数p表示为561417,进制为8。则p的十进制表示为
5
∗
8
5
+
6
∗
8
4
+
1
∗
8
3
+
4
∗
8
2
+
1
∗
8
1
+
7
∗
8
0
=
189199
5*8^{5}+6*8^{4}+1*8^{3}+4*8^{2}+1*8^{1}+7*8^{0}=189199
5∗85+6∗84+1∗83+4∗82+1∗81+7∗80=189199
假设是对5取余数,此时有
- (561417)8= ( 5 ∗ 8 5 + 6 ∗ 8 4 + 1 ∗ 8 3 + 4 ∗ 8 2 + 1 ∗ 8 1 + 7 ∗ 8 0 ) m o d 5 (5*8^{5}+6*8^{4}+1*8^{3}+4*8^{2}+1*8^{1}+7*8^{0})mod5 (5∗85+6∗84+1∗83+4∗82+1∗81+7∗80)mod5
- = ( ( 5 ∗ 8 5 ) m o d 5 + ( 6 ∗ 8 4 ) m o d 5 + ( 1 ∗ 8 3 ) m o d 5 + ( 4 ∗ 8 2 ) m o d 5 + ( 1 ∗ 8 1 ) m o d 5 + ( 7 ∗ 8 0 ) m o d 5 ) m o d 5 ((5*8^{5})mod5+(6*8^{4})mod5+(1*8^{3})mod5+(4*8^{2})mod5+(1*8^{1})mod5+(7*8^{0})mod5)mod5 ((5∗85)mod5+(6∗84)mod5+(1∗83)mod5+(4∗82)mod5+(1∗81)mod5+(7∗80)mod5)mod5
- = ( 5 m o d 5 ∗ ( 8 m o d 5 ) 5 m o d 5 ) m o d 5 + ( 6 m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 ) m o d 5 + ( 1 m o d 5 ∗ ( 8 m o d 5 ) 3 m o d 5 ) m o d 5 + ( 4 ∗ 8 2 m o d 5 ) m o d 5 + ( 1 ∗ 8 1 m o d 5 ) m o d 5 + ( 7 m o d 5 ∗ ( 8 m o d 5 ) 0 m o d 5 ) m o d 5 ) m o d 5 (5mod5*(8mod5)^{5}mod5)mod5+(6mod 5*(8mod5)^{4}mod5)mod5+(1mod5*(8mod5)^{3}mod5)mod5+(4*8^{2}mod5)mod5+(1*8^{1}mod5)mod5+(7mod5*(8mod5)^{0}mod5)mod5)mod5 (5mod5∗(8mod5)5mod5)mod5+(6mod5∗(8mod5)4mod5)mod5+(1mod5∗(8mod5)3mod5)mod5+(4∗82mod5)mod5+(1∗81mod5)mod5+(7mod5∗(8mod5)0mod5)mod5)mod5
这步之后我们先看前两位
- = ( 5 m o d 5 ∗ ( 8 m o d 5 ) 5 m o d 5 + 6 m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 ) m o d 5 + . . . (5mod5*(8mod5)^{5}mod5+6mod 5*(8mod5)^{4}mod5)mod5+... (5mod5∗(8mod5)5mod5+6mod5∗(8mod5)4mod5)mod5+...
- = ( 5 m o d 5 ∗ 8 m o d 5 + 6 m o d 5 ) m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 ) m o d 5 + . . . (5mod5*8mod5+6mod 5)mod5*(8mod5)^{4}mod5)mod5+... (5mod5∗8mod5+6mod5)mod5∗(8mod5)4mod5)mod5+...
- = ( 5 m o d 5 ∗ 8 + 6 ) m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 + . . . (5mod5*8+6)mod5*(8mod5)^{4}mod5+... (5mod5∗8+6)mod5∗(8mod5)4mod5+...
此时可以看出结果了:前
i
−
1
i-1
i−1位向前
i
i
i位转换时,只需要对上一个状态(
5
m
o
d
5
5mod5
5mod5的状态)乘上一个进制,再加上下一位,不断取模即可。
我们继续转换:
- = ( 5 m o d 5 ∗ 8 + 6 ) m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 + ( 1 m o d 5 ∗ ( 8 m o d 5 ) 3 m o d 5 ) m o d 5 + . . . (5mod5*8+6)mod5*(8mod5)^{4}mod5+(1mod5*(8mod5)^{3}mod5)mod5+... (5mod5∗8+6)mod5∗(8mod5)4mod5+(1mod5∗(8mod5)3mod5)mod5+...
- = ( ( 5 m o d 5 ∗ 8 + 6 ) m o d 5 ∗ ( 8 m o d 5 ) 4 m o d 5 + ( 1 m o d 5 ∗ ( 8 m o d 5 ) 3 m o d 5 ) ) m o d 5 + . . . ((5mod5*8+6)mod5*(8mod5)^{4}mod5+(1mod5*(8mod5)^{3}mod5))mod5+... ((5mod5∗8+6)mod5∗(8mod5)4mod5+(1mod5∗(8mod5)3mod5))mod5+...
- = ( ( 5 m o d 5 ∗ 8 + 6 ) m o d 5 ∗ ( 8 m o d 5 ) + 1 m o d 5 ) m o d 5 ∗ ( ( 8 m o d 5 ) 3 m o d 5 ) m o d 5 + . . . ((5mod5*8+6)mod5*(8mod5)+1mod5)mod5*((8mod5)^{3}mod5)mod5+... ((5mod5∗8+6)mod5∗(8mod5)+1mod5)mod5∗((8mod5)3mod5)mod5+...
- = ( ( ( 5 m o d 5 ∗ 8 + 6 ) ∗ 8 m o d 5 ) + 1 m o d 5 ) m o d 5 ∗ ( 8 m o d 5 ) 3 m o d 5 + . . . (((5mod5*8+6)*8mod5)+1mod5)mod5*(8mod5)^{3}mod5+... (((5mod5∗8+6)∗8mod5)+1mod5)mod5∗(8mod5)3mod5+...
- =…
最后会处理到 1 m o d 5 1mod5 1mod5,这个结果是1,也就直接被忽略了。 - = ( . . . . ) ∗ ( 8 m o d 5 ) 0 m o d 5 (....)*(8mod5)^{0}mod5 (....)∗(8mod5)0mod5
- = ( . . . . ) ∗ 1 m o d 5 (....)*1mod5 (....)∗1mod5
- = ( . . . . ) m o d 5 (....)mod5 (....)mod5
也就是说,一个任意进制的数对x取余的结果等于其各个数位上的数(转化进制之后),对x取余的结果的和,再对x取余。
利用这一点,我们可以依次对数p的各个位置上的数进行取余,并与上一次取余运算结果构成两位的新数,乘上进制之后相加,再取余。也就是代码段
long long px = 0;
for (int i = 0; i < p.length();i++){
px = px * b + p[i] - '0'; // 还原两位
px %= mx; // 计算这两位的取模结果
}
的处理方式。
细节提示
注意输出时假如结果的px为0时的输出。
参考代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int b;
string p, m;
while(cin>>b){
if (b == 0)
break;
else{
cin >> p >> m;
long long mx = 0;
for (int i = 0; i < m.length();i++)
mx = mx * b + m[i] - '0';
long long px = 0;
for (int i = 0; i < p.length();i++){
px = px * b + p[i] - '0';
px %= mx;
}
if(px==0)
cout << 0 << endl;
else{
string ans;
while(px>0){
ans += to_string(px % b);
px /= b;
}
reverse(ans.begin(), ans.end());
cout << ans << endl;
}
}
}
return 0;
}
问题 F: 5206 进制转换
题目描述
编写程序,实现将一个数从一种进制转换到另一种进制。在这些进制中,可以出现的数码有62个:{0-9,A-Z,a-z}。
输入
输入文件的第1行是一个正整数N,表示测试数据的个数,接下来有N行,每行的格式为:输入数据的进制(用十进制表示),输出数据的进制(用十进制表示),最后一个是用输入数据的进制所表示的数。输入/输出数据的进制范围是262,也就是说AZ相当于十进制中的1035,az相当于十进制中的36~61。
输出
对每个测试数据,程序输出3行,第1行是输入数据的进制,空格,然后是在该进制下的输入数据;第2行是输出数据的进制,空格,然后是在该进制下的输出数据;第3行为空行。
样例输入
8
62 2 abcdefghiz
10 16 1234567890123456789012345678901234567890
16 35 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 23 333YMHOUE8JPLT7OX6K9FYCQ8A
23 49 946B9AA02MI37E3D3MMJ4G7BL2F05
49 61 1VbDkSIMJL3JjRgAdlUfcaWj
61 5 dl9MDSWqwHjDnToKcsWE1S
5 10 42104444441001414401221302402201233340311104212022133030
样例输出
62 abcdefghiz
2 11011100000100010111110010010110011111001001100011010010001
10 1234567890123456789012345678901234567890
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 333YMHOUE8JPLT7OX6K9FYCQ8A
35 333YMHOUE8JPLT7OX6K9FYCQ8A
23 946B9AA02MI37E3D3MMJ4G7BL2F05
23 946B9AA02MI37E3D3MMJ4G7BL2F05
49 1VbDkSIMJL3JjRgAdlUfcaWj
49 1VbDkSIMJL3JjRgAdlUfcaWj
61 dl9MDSWqwHjDnToKcsWE1S
61 dl9MDSWqwHjDnToKcsWE1S
5 42104444441001414401221302402201233340311104212022133030
5 42104444441001414401221302402201233340311104212022133030
10 1234567890123456789012345678901234567890
解析
思路梳理
此题数据都比较大,建议按字符串输入后直接用数组储存各个位置上的数值大小。
核心代码
核心代码是通过模仿整除法的步骤来进行更迭循获得结果的。
int k, val = 1;
//定义一个val并赋值为1,赋值为1以保证进入while循环
//val用于记录记录数据的数组nx中数位数值的和
string ans;//ans用于记录每次除法的余数
while (val != 0){
val = 0;
int nut = 0;//nut用于取从高位起的用来做除法的数。
k = 0;
nut = 0;
for (i = 0; i < len;){
nut = nut * in + nx[i++];
//nut需要加上从上一次除法结束时的商再做除法。
nx[k++] = nut / ot;
//nx进行数据重置,新填入的数是整个原数做除法之后的商。
val += nx[k - 1];
nut %= ot;
}
len = k;
/*将余数储存起来*/
if (nut < 10)
ans.push_back(char(nut + '0'));
else if (nut >= 10 && nut <= 35)
ans.push_back(char(nut - 10 + 'A'));
else if (nut >= 36 && nut <= 61)
ans.push_back(char(nut - 36 + 'a'));
}
细节提示
注意0~9转为字符时要加上’0’。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1000;
int nx[N], cx[N];
int main(){
int t;
cin >> t;
int in, ot;
string num;
while(t--){
cin >> in >> ot >> num;
cout << in << " " << num << endl;
cout << ot << " ";
int i = 0, len = num.length();
for (; i < len; i++){
if(num[i]-'0'<10)
nx[i] = num[i] - '0';
else if(num[i]>='A'&&num[i]<='Z')
nx[i] = num[i] - 'A' + 10;
else if(num[i]>='a'&&num[i]<='z')
nx[i] = num[i] - 'a' + 36;
}
int k, val = 1;
string ans;
while (val != 0){
val = 0;
int nut = 0;
k = 0;
nut = 0;
for (i = 0; i < len;){
nut = nut * in + nx[i++];
nx[k++] = nut / ot;
val += nx[k - 1];
nut %= ot;
}
len = k;
if (nut < 10)
ans.push_back(char(nut + '0'));
else if (nut >= 10 && nut <= 35)
ans.push_back(char(nut - 10 + 'A'));
else if (nut >= 36 && nut <= 61)
ans.push_back(char(nut - 36 + 'a'));
}
reverse(ans.begin(), ans.end());
cout << ans << endl
<< endl;
}
return 0;
}