例题1——进制转换(十进制---->二进制)
题目描述:将超大的十进制正整数,转化为对应的二进制形式。
解题思路:这题主要的难点,是十进制数的范围很大。
解题阶段一
用字符串形式来存储数字,不断进行对2取模和对2整除运算,以期求出结果。
但是提交代码进行在线测试的时候,出现超时的报错。
我当时以为这种算法本身就比较费时,就没有再深究代码bug,换了其他的思路。然而后面发现,代码的strDiv函数存在各种漏洞,详细请见解题阶段三。
#include <iostream>
using namespace std;
string strDiv(string str,int& mod){ // 这里n是1~9的整数
int remainder=0;
string res="";
char temp;
for(int i=0;i<str.size();i++){
remainder*=10;
remainder += (str[i]-'0');
temp = remainder/2 + '0';
res+=temp;
remainder%=2;
}
mod = remainder;
return res;
}
int main(){
string n;
while(cin>>n){
string ans="";
char temp;
int mod;
if(n=="0"){
cout<<"0"<<endl;
continue;
}
while(n!="0"){
n=strDiv(n,mod);
temp = mod+'0';
ans = temp + ans;
}
cout<<ans<<endl;
}
return 0;
}
解题阶段二
- 从需要转换的十进制数找到与之最接近的2的幂次方,并从这个十进制数中减去 该2的幂次方,在剩下的余数中重复这种做法,直到余数为0。
- 然后将所得到的这些2的幂次方与二进制数中的位权相比,相同的位标记为1,其余的位0,这样就可得到目标结果。
需要注意的是,有关string形式的运算操作,需要自己手写函数。我以为这种方法还挺快的,但是在线测试的时候发现,虽然代码通过了测试,但是效率与预期相比还是稍微有点低。应该是自己手写的函数,还不够简洁。
#include <iostream>
#include <queue>
using namespace std;
const int MAXN=101;
int strCmp(string str1,string str2){ // 两个数字字符串大小的比较,str1<str2: -1, str1==str2: 0, str1>str2: 1
int len1 = str1.size();
int len2 = str2.size();
if(len1<len2) return -1;
else if(len1>len2) return 1;
else{
if(str1<str2) return -1;
else if(str1>str2) return 1;
else return 0;
}
}
string strSub(string str,string sub){ // 返回str - sub 的结果,这里保证str的数值大于sub的数值
int carry = 0,j=str.size()-1;
for(int i=sub.size()-1;i>=0&&j>=0;i--,j--){
int elem1,elem2;
elem1=str[j]-'0'+carry;
elem2=sub[i]-'0';
carry=0;
if(elem1<elem2){
carry=-1;
elem1+=10;
}
str[j]=(elem1-elem2) + '0';
}
while(carry==-1&&j>=0){
int elem = str[j]-'0';
elem+=carry;
if(elem>=0) {
str[j] = elem + '0';
carry=0;
}else{
elem+=10;
str[j] = elem + '0';
j--;
}
}
int pos=0; // 寻找首个非0下标
while(str[pos]=='0'){
pos++;
}
if(pos==str.size()) return "0";
return str.substr(pos);
}
string strMul(string str,int n){ // n暂时限制为1~9之间的整数
int carry=0;
string res="";
char temp;
for(int i=str.size()-1;i>=0;i--){
carry+=(str[i]-'0')*n;
temp = carry%10+'0';
res = temp+res;
carry/=10;
}
while(carry>0){
temp = carry%10+'0';
res = temp+res;
carry/=10;
}
int pos=0; // 寻找首个非0下标
while(str[pos]=='0'){
pos++;
}
if(pos==str.size()) return "0";
return str.substr(pos);
}
string arr[MAXN];
void Initial(){
arr[0]="1";
for(int i=1;i<MAXN;i++){
arr[i] = strMul(arr[i-1],2);
}
}
int main(){
Initial();
string input;
while(cin>>input){
queue<int> myQueue;
for(int i=MAXN-1;i>=0;i--){
if(strCmp(input,arr[i])!=-1){
myQueue.push(i);
input = strSub(input,arr[i]);
}
}
if(myQueue.empty()){
cout<<"0"<<endl;
continue;
}
int maxPos = myQueue.front();
myQueue.pop();
string ans="1";
for(int i=maxPos-1;i>=0;i--){
if(myQueue.empty()==true || myQueue.front()!=i) ans+="0";
else{
myQueue.pop();
ans+="1";
}
}
cout<<ans<<endl;
}
return 0;
}
解题阶段三
看了王道机试指南的题解,才发现解题阶段一的思路其实可行的,把strDiv函数中的bug完善好(bug是:未考虑到str为"00000"这种情况,需要在函数末尾处进行对应的处理),就能通过测试,代码比较简洁,而且在测试中耗时也小了一些。
这给我的一个教训是:
自己手写的函数,一定要单独多测试几遍,把一些容易出错的边界数据拿来测试,不要一昧求快,草草应付测试。
#include <iostream>
using namespace std;
string strDiv(string str) { // 这里n是1-9的整数,是
int remainder=0;
string res="";
char temp;
for(int i=0; i<str.size(); i++) {
remainder*=10;
remainder += (str[i]-'0');
temp = remainder/2 + '0';
res+=temp;
remainder%=2;
}
int pos=0; // 寻找首个非0下标
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size())
return "0";
return res.substr(pos);
}
int main() {
string n;
while(cin>>n) {
if(n=="0") {
cout<<"0"<<endl;
continue;
}
string ans="";
while(n!="0") {
char temp;
temp = (n[n.size()-1]-'0')%2 + '0';
n=strDiv(n);
ans = temp + ans;
}
cout<<ans<<endl;
}
return 0;
}
例题2——10进制 VS 2进制(十进制<---->二进制)
#include <iostream>
using namespace std;
string strMul(string str, int n){ // 乘法 n是1~9的整数
int carry=0,len=str.size();
string res="";
for(int i=len-1;i>=0;i--){
carry = carry+(str[i]-'0')*n;
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
while(carry>0){
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res;
}
string strAdd(string str1,string str2){ // 加法 假定str1的长度 >= str2的长度
if(str1.size()<str2.size())
swap(str1,str2);
int carry=0,len1=str1.size(),len2=str2.size(),j,i;
string res="";
for(i=len1-1,j=len2-1;i>=0&&j>=0;i--,j--){
carry = carry + (str1[i]-'0') + (str2[j]-'0');
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
while(carry!=0){
if(i>=0){
carry = carry + (str1[i]-'0');
i--;
}
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
if(i>=0){
res = str1.substr(0,i+1) + res;
}
int pos = 0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
string strDiv(string str,int n){ // 除法 n是1~9的整数
int remainder=0,len=str.size();
string res="";
for(int i=0;i<len;i++){
remainder*=10;
remainder+=(str[i]-'0');
char temp = remainder/n + '0';
res = res + temp;
remainder%=n;
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
int main() {
/*string str="1";
for(int i=1;i<=6000;i++){
str = strMul(str,2);
if(str.size()>1000){ // 此时i=3322
cout<<i<<endl;
break;
}
}*/
string input;
while(cin>>input){
if(input=="0"){
cout<<"0"<<endl;
continue;
}
string erjinzhi="";
while(input!="0"){
char temp = ((input[input.size()-1]-'0')%2) +'0';
erjinzhi = temp + erjinzhi;
input = strDiv(input,2);
}
int len=erjinzhi.size();
int pos = len-1;
while(pos>=0&&erjinzhi[pos]=='0')
pos--;
string ans="0",elem="1";
for(int i=0;i<=pos;i++){
if(erjinzhi[i]=='1')
ans = strAdd(ans,elem);
elem = strMul(elem,2);
}
cout<<ans<<endl;
}
return 0;
}
例题3——进制转换2(M进制---->N进制)
#include <iostream>
using namespace std;
/*
注意输入时如有字母,则字母为大写;
输出时如有字母,则字母为小写
*/
string strAdd(string str1,string str2){ // 十进制(没有前导0) 加法 认为str1是长度较长的那个
if(str1.size()<str2.size()) swap(str1,str2);
int len1=str1.size(),len2=str2.size(),carry=0,i,j;
string res="";
for(i=len1-1,j=len2-1;i>=0&&j>=0;i--,j--){
carry += (str1[i]-'0') + (str2[j]-'0');
char temp = (carry%10) + '0';
res = temp + res;
carry/=10;
}
while(i>=0){
carry += (str1[i]-'0');
char temp = (carry%10) + '0';
res = temp + res;
carry/=10;
i--;
}
while(carry>0){
char temp = (carry%10) + '0';
res = temp + res;
carry/=10;
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
string strMul(string str,int n){ // 十进制的乘法 n应该是一个较小的正整数
int carry=0;
string res="";
for(int i=str.size()-1;i>=0;i--){
carry += (str[i]-'0')*n;
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
while(carry>0){
char temp = carry%10 + '0';
res = temp + res;
carry/=10;
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
string strDiv(string str,int n,int& mod){ // 十进制的除法 n应该是一个较小的正整数
int remainder=0;
string res="";
char temp;
for(int i=0;i<str.size();i++){
remainder*=10;
remainder+=str[i]-'0';
temp = remainder/n +'0';
res+=temp;
remainder %= n;
}
mod = remainder;
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
string TranFromX(string str,int x){ // 将x进制的数str转换为10进制数,先决条件:str是合法的x进制数
string elem="1",res="0";
for(int i=str.size()-1;i>=0;i--){
int temp;
if(str[i]>='A') temp=(str[i]-'A'+10);
else temp=(str[i]-'0');
res = strAdd(res,strMul(elem,temp));
elem = strMul(elem,x);
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
string TranToX(string str,int x){ // 将10进制的数str转换为x进制数
string res="";
int mod;
char temp;
while(str!="0"){
str=strDiv(str,x,mod);
if(mod>9) temp=mod-10+'a';
else temp = mod+'0';
res=temp+res;
}
int pos=0;
while(pos<res.size()&&res[pos]=='0')
pos++;
if(pos==res.size()) return "0";
return res.substr(pos);
}
int main(){
int M,N;
while(cin>>M>>N){
string input;
cin>>input;
cout<<TranToX(TranFromX(input,M),N)<<endl;
}
return 0;
}