zoukankan      html  css  js  c++  java
  • 【数论/递归】P1017 进制转换

    原题:https://www.luogu.com.cn/problem/P1017

    (不再粘贴题目了,太麻烦)

    P1017 进制转换 【普及/提高-】 题解

    第一部分:痛苦与绝望

    经过了两个小时的煎熬,这道题基本可以算是将近把我逼疯了。看着其他同学的五一假期作业分数早已经突破800分,【提高+/省选-】的题一遍就过,我却还在400分左右徘徊,这样一道黄题都过不了......

    废话不多说,进入正题

    这是我耗了两个小时的成果:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    vector<int> a;
    int n,R,len;
    pair<int,int> range[100];
    char vir[21]="0123456789ABCDEFGHIJ";
    int main()
    {
        cin>>n>>R;
        range[0].first=0;
        range[0].second=-R;
        for(int i=0; pow(-R,i)<=2147483647; i++)
        {
            if(i%2==1)
            {
                range[i].first=range[i-1].first-pow(-R,i);
                range[i].second=range[i-1].second;
            }
            else if(i%2==0)
            {
                range[i].first=range[i-1].first;
                range[i].second=range[i-1].second+pow(-R,i);
            }
            if(n<=range[i].second&&n>=range[i].first)
            {
                len=i;
                break;
            }
        }
    //    cout<<len<<endl;
        cout<<n<<"=";
        for(int i=len+1; i>=1; i--)
        {
            if((i-1)%2==0&&n>0)
            {
                for(int j=0;; j++)
                {
                    if(j*pow(-R,i-1)>=n||j==-R-1)
                    {
                        //cout<<pow(-R,i-1)<<endl<<n<<endl;
                        cout<<vir[j];
                        n -= j*pow(-R,i-1);
                        break;
                    }
                }
            }
            else if((i-1)%2==1&&n<0)
            {
                for(int j=0;; j++)
                {
                    if(-(j)*pow(-R,i-1)<=n||j==-R-1)
                    {
                    //    cout<<-pow(-R,i-1)<<endl<<n<<endl;
                        cout<<vir[j];
                        n += j*pow(-R,i-1);
                        break;
                    }
                }
            }
            else
            {
                cout<<0;
                continue;
            }
        }
        cout<<"(base"<<R<<")"<<endl;
        return 0;
    }

    然而命运多舛,如果给定的数是负数,这个代码就无能为力了。我将近哭了出来,心想难道这就是智力不够吗......就是因为它,还有其他同学遥遥领先的进度,我在网课上将近崩溃,不得不请假离开......

    第二部分:清醒与悔悟

    痛定思痛,结合题解,我认真地反思了一下。

    把十进制转换成其他进制,最基础的方法是什么?

    不就是用原十进制数一步步除以要转换的进制数,将余数倒序输出么!这题虽然有负数,但思路一定是同样的,只需要做出一些微调就行!

    然而,从一开始,我的思路就错了。我先从“大”处理,就是先算出转换后的位数,然后一步步调用pow(求幂函数),一开始就会出现很大的数字,然后再求值、取余......殊不知这已经远远偏离了正确思路。

    在数次痛苦的哀嚎后,我才明白,这是无济于事的。我需要解决问题。

    经过一次次心理的狂风巨浪以后,我的思路正在向真理接近......

    第三部分:希望与涅槃

    以下是正确的思路。

    对于非负数进制的转换(原数是十进制),设要转换成 R 进制,只要每次将原数除以 R ,得到一系列余数 m1 , m2 , m3 ... m,然后再按照逆着的顺序输出 mn ,mn-1 , ... , m3 , m2 m1。

    也就是说,我们只需要取余,从原数减去 余数乘以进制数 R 的积 ,然后继续往下寻找(递归)就行。

    //版本1
    void turn(int n,int r)//n是当前需要模的数字,r是模数
    {
        if(n==0) return;
        int m=n%r;
        存储m;
        turn(n/r,r);//下一步
    }
    int main()
    {
        输入n,r;
        turn(n,r);
        倒序输出所有存储的m;
    }
    //版本2
    void turn(int n,int r)
    {
        if(n==0)return;
        int m=n%r;
        turn(n/r,r);
        cout<<m;//在递归后输出,能保证倒序
    }

    但是对于这道题,很明显其中会出现负数。对于被模数是负数的情况,计算机会返回一个负数,例如:-5 % -3 = -2 , -5 % 3 = -2。

    负数余数是我们不想要的情况,需要把它转换成正数。

    经过很长时间的探究后,我发现了规律:如果出现负数余数,那么就把它减去负进制数 R (R < 0),这样会使它成为正数,然后进行下一步递归时,将 n 除以 r 所得的商加上 1 :

    m-=r;
    turn(n/r+1,r);

    然后再根据正数情况处理就行了。

    我的代码运用STL模板,具有很高的整合性。我的想法:单独地编写一个取余函数,能够处理负数余数。然后返回一个pair,一个存储得到的余数,一个存储一个bool值,表示它是不是遇到了负数的情况,也就是商需不需要加一。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<stack>
    using namespace std;
    char vir[21]="0123456789ABCDEFGHIJ";
    int N,R;
    stack<int> st;//栈具有后进先出的性质,适合倒序输出 
    pair<int,bool> ultra_mod(int n,int r)
    {
        int mod=n%r;
        pair<int,bool> res;
        if(mod<0)
        {
            mod-=r;//处理负余数 
            res.second=true;//有负余数,需要在下一步递归时+1 
        } 
        else res.second=false;//不需要+1 
        res.first=mod;//导出余数 
        return res;
    }
    void turn(int n,int r)
    {
        if(n==0) return;//停止递归 
        pair<int,bool> mod=ultra_mod(n,r);//获得余数信息 
        st.push(mod.first);//将余数压进栈 
        if(mod.second == true) turn(n/r+1,r);//遇到负数情况 
        else turn(n/r,r);
    }
    int main()
    {
        cin>>N>>R;
        turn(N,R);
        cout<<N<<"=";
        while(!st.empty())
        {
            cout<<vir[st.top()];//倒序输出所有余数(若大于9先转换成字母) 
            st.pop();
        }
        cout<<"(base"<<R<<")";
        return 0;
    }
  • 相关阅读:
    Android:求职旺季,了解这份面试经验,自然无惧面试!
    2020上半年百度Android岗(初级到高级)面试真题全收录+解析,备战金九银十!(下篇)_chuhe1989的博客-CSDN博客
    泪目!阿里大佬国庆8天花了50个小时,整理出这份18万字Android-360&#176;性能优化实战解析_chuhe1989的博客-CSDN博客
    Android程序员面试必须要掌握的:Https加密原理、中间人攻击到底是怎么回事_chuhe1989的博客-CSDN博客
    @HTTPS 原理分析——带着疑问层层深入
    @Android 换肤那些事儿, Resource包装流 ?AssetManager替换流?
    @腾讯面试官:说说王者荣耀里面Android的换肤原理和Android的皮肤,装载机框架解析
    @Android 无缝换肤深入了解与使用
    @Android的换肤原理和Android的皮肤,装载机框架解析
    @Android小技巧--switch控件的用法
  • 原文地址:https://www.cnblogs.com/jiangyuechen/p/12822743.html
Copyright © 2011-2022 走看看