【题目链接】
OpenJudge NOI 1.13 44:正整数的任意进制转换
注意:题目说是正整数的进制转换,而实际上输入的数字可能有0。
【题目考点】
1. 数制
2. 高精度
【解题思路】
先处理字符串,将输入的字符串拆分成p,p进制字符串n,及q。
遍历字符串,遇到逗号时,截取一段字符串。将截取出的第一和第三段字符串转为整数,就是p与q,第二段字符串为数字串。
先考虑如果涉及的都是低精度数字,应该如何将p进制的n转为q进制。
先将p进制的n转为值v,再对v在q进制下做数字拆分,得到q进制下的数字串。
#include<bits/stdc++.h>
using namespace std;
int p, q;
string n;
void parse(string s)//解析输入的字符串s
{
string t;
int c = 0;//记录上个逗号的位置
for(int i = 0; i < s.length(); ++i)
{
if(s[i] == ',')
{
if(c == 0)//如果是第一个逗号
{
t = s.substr(0, i);//从0开始取i个
p = stoi(t);//这个子串为第一个进制数字
c = i;
}
else//如果是第二个逗号
{
n = s.substr(c+1, i-c-1);//c是上一个逗号的位置,数字串第一个字符的位置为c+1,此时第二个逗号位置为i,数字串长度为i-c-1
t = s.substr(i+1);//从i+1到末尾为第二个进制数字
q = stoi(t);
break;
}
}
}
}
int getVal(char c)//字符转数值
{
if(c >= '0' && c <= '9')
return c-'0';
else
return c-'A'+10;
}
char getCh(int n)//数值转字符
{
if(n >= 10)
return n-10+'A';
else
return n+'0';
}
int toVal(string n)
{
int num = 0;
for(int i = 0; i < n.length(); ++i)
num = num*p + getVal(n[i]);
return num;
}
string toStr(int v)
{
string r;
int a = v;
while(a > 0)//q进制下的数字拆分
{
r = getCh(a%q) + r;
a /= q;
}
return r;
}
int main()
{
string s;
cin >> s;
parse(s);
int v = toVal(n);
cout << toStr(v);
return 0;
}
以上代码可以解决数值在int类型范围内的数字的数制转换。
而本题n可以达到50位,必须用高精度数字表示。
将输入的数字串转为p进制高精度数字。
上述代码中“将p进制数字转为值”这一步不用做了,因为不存在可以表示这个值的基本数据类型。
直接将p进制高精度数字,转为q进制高精度数字,其逻辑与toStr函数的逻辑是相同的。
先让高精度数字a的值为p进制高精度数字n的值。只要a不为0,每次让a除以q(高精除低精),得到余数r(低精度)作为q进制数字串的一位,将得到的商再赋值给a。循环执行,直到a为0。这样就得到了q进制字符串。
注意:数字拆分写法for(int a = n; a > 0; a /= 10)
只能在n大于0时才能使用。这里的高精度数字n仍然可能是等于0的(虽然题目说了“正整数”,但实际数据中存在0),因此对于数字n是0的情况要做特判。
考虑极端情况如果36进制数字“50位Z”转为二进制数字,得到的数字位数可以达到300位。
【题解代码】
解法1:使用数字数组
#include<bits/stdc++.h>
using namespace std;
#define N 55
int p, q;
string n;
void parse(string s)//解析输入的字符串s,得到p,q,n
{
string t;
int c = 0;//记录上个逗号的位置
for(int i = 0; i < s.length(); ++i)
{
if(s[i] == ',')
{
if(c == 0)//如果是第一个逗号
{
t = s.substr(0, i);//从0开始取i个
p = stoi(t);//这个子串为第一个进制数字
c = i;
}
else//如果是第二个逗号
{
n = s.substr(c+1, i-c-1);//c是上一个逗号的位置,数字串第一个字符的位置为c+1,此时第二个逗号位置为i,数字串长度为i-c-1
t = s.substr(i+1);//从i+1到末尾为第二个进制数字
q = stoi(t);
break;
}
}
}
}
int getVal(char c)//字符转数值
{
if(c >= '0' && c <= '9')
return c - '0';
else
return c - 'A' + 10;
}
char getCh(int n)//数值转字符
{
if(n >= 10)
return n - 10 + 'A';
else
return n + '0';
}
void toNum(string s, int a[])//将数字串n转为数字数组a
{
a[0] = s.length();
for(int i = 1; i <= s.length(); ++i)
a[i] = getVal(s[s.length()-i]);
}
void numCpy(int a[], int b[])//数字数组b拷贝给a
{
for(int i = 0; i <= b[0]; ++i)
a[i] = b[i];
}
int Divide(int a[], int b, int k)//a /= b,k进制高精度数字a除以低精度数字b,返回值为余数
{
int r[N] = {
}, m = 0;//r:商 m:中间临时使用的数字,是上一次小规模除法的余数,也是下一次的被除数
for(int i = a[0]; i >= 1; --i)
{
r[i] = (m * k + a[i]) / b;
m = (m * k + a[i]) % b;
}
int r_i = a[0];
while(r[r_i] == 0 && r_i > 1)
r_i--;
r[0] = r_i;
numCpy(a, r);//把商赋值给a
return m;
}
string toStr(int num[])//p进制高精度数字num转为q进制字符串
{
string r;
int a[N], c;
if(num[0] == 1 && num[1] == 0)//如果p进制高精度数字为0
return "0";//直接返回字符串0
numCpy(a, num);
while(!(a[0] == 1 && a[1] == 0))//只要高精度数字a不是0
{
c = Divide(a, q, p);//p进制数字a /= q,余数为c
r = getCh(c) + r;
}
return r;
}
int main()
{
int t, num[N];
string s;
cin >> t;
while(t--)
{
cin >> s;
parse(s);
toNum(n, num);//将数字串n转为数字数组num
cout << toStr(num) << endl;
}
return 0;
}
解法2:高精度数字类
#include<bits/stdc++.h>
using namespace std;
#define N 305 //注意数字36进制数字50位Z转换为二进制后最多可以达到300位
struct HPN
{
int a[N], k;//a:数字数组 k:进制
HPN(){
}
HPN(string s, int _k)
{
a[0] = s.length();
for(int i = 1; i <= s.length(); ++i)
a[i] = getVal(s[s.length()-i]);
k = _k;
}
int getVal(char c)//字符转数值
{
return isdigit(c) ? c - '0' : c - 'A' + 10;
}
char getCh(int n)//数值转字符
{
return n >= 10 ? n - 10 + 'A' : n + '0';
}
int operator [] (int i)
{
return a[i];
}
void operator /= (int b)//a /= b 高精除低精
{
int m = 0, d;//m:中间临时使用的数字,是上一次小规模除法的余数,也是下一次的被除数
for(int i = a[0]; i >= 1; --i)
{
d = m * k + a[i];//临时被除数
m = d % b;
a[i] = d / b;
}
int l = a[0];
while(a[l] == 0 && l > 1)
l--;
a[0] = l;
}
int operator % (int b)//a%b 高精模低精
{
int m = 0;//m:中间临时使用的数字,是上一次小规模除法的余数,也是下一次的被除数
for(int i = a[0]; i >= 1; --i)
m = (m * k + a[i]) % b;
return m;
}
void changeSys(int x)//将当前数字的进制从k改为x
{
HPN t = *this;//zero:高精度数字0,t先设为与自己相同
k = x;
if(a[0] == 1 && a[1] == 0)//如果自己本身是0
return;//不用操作
int c, ai = 0;
while(!(t[0] == 1 && t[1] == 0))//只要高精度数字t不是0
{
a[++ai] = t % x;//高精模低精:t%x的结果填充到自己的数字数组a中
t /= x;//高精除低精
}
a[0] = ai;
}
void show()
{
for(int i = a[0]; i >= 1; --i)
cout << getCh(a[i]);
cout << endl;
}
};
void parse(string s, string &n, int &p, int &q)//解析输入的字符串s,得到p,q,n
{
int c1 = 0, c2 = 0;//c1, c2:第1、第2逗号的位置
for(int i = 0; i < s.length(); ++i)
{
if(s[i] == ',')
{
if(c1 == 0)
c1 = i;
else
c2 = i;
}
}
p = stoi(s.substr(0, c1));
q = stoi(s.substr(c2+1));
n = s.substr(c1+1, c2-c1-1);
}
int main()
{
HPN num;
int p, q, t;
string s, n;
cin >> t;
while(t--)
{
cin >> s;
parse(s, n, p, q);
num = HPN(n, p);//p进制高精度数字num
num.changeSys(q);
num.show();
}
return 0;
}