王道机试指南--第四章(数学问题)

数制转换

题目链接数制转换

题目大意:由a进制转换为b进制,其中(2<=a,b<=16)。
思路:将a数制先转换为10进制,然后10进制转换为b进制。
代码如下:

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX = 200;
vector<char> v;
void solve10tom(long long a, int m)//十进制转换为m进制
{
    int b;
    char t;
    while(a)
    {
        b = a%m;
        if(b>=10)
            t = 'A'+b-10;
        else
            t = b+'0';
        v.push_back(t);
        a /= m;
    }
    for(int i=v.size()-1; i>=0; i--)
    {
         cout << v[i];
    }
    cout << endl;
    v.clear();
}
long long solvemto10(char s[], int m)//m进制转换为10进制
{
    long long p = 0, d = 0;
    for(int i=0; i<strlen(s); i++)
    {
        if(s[i]>='a' && s[i]<='f')
            d = 10+s[i]-'a';
        else if(s[i]>='A' && s[i]<='F')
            d = 10+s[i]-'A';
        else
            d = s[i]-'0';
        p = p*m + d;
    }
    return p;
}
int main()
{
    int a, b;
    char s[MAX];
    while(scanf("%d%s%d", &a, s, &b)!=EOF)
    {
        long long t = solvemto10(s, a);
        if(t==0) cout << 0 << endl;
        else     solve10tom(t, b);
    }
    return 0;
}
进制转换

题目链接进制转换
题目大意:30位的十进制数转换为二进制。
思路:难点是位数较多,只能存入数组,模仿手算的过程。
参考博文:九度OJ 1138 进制转换
代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int MAX = 200;
char s[MAX];
int input[MAX], output[MAX];

int main()
{
    while(scanf("%s", s)!=EOF)
    {
        for(int i=0; i<strlen(s); i++)//将字符数组转换为数组
        {
            input[i] = s[i]-'0';
        }

        int k = 0, sum = 1, d, len = strlen(s);
        while(sum)//当sum为0时代表计算已结束
        {
            sum = 0;
            for(int i=0; i<len; i++)//遍历计算数组,从高位到低位
            {
                d = input[i]/2;//此位计算后的值,先存下
                sum += d;//如果此位为0,则加0
                if(i==len-1)//最后一位的决定余数
                    output[k++] = input[i]%2;
                else//此位的余数可以在计算下一位的时候用的上
                    input[i+1] += (input[i]%2)*10;
                input[i] = d;
            }
        }
        if(!k) cout << 0 << endl;
        else
        {
            for(int i=k-1; i>=0; i--)
                cout << output[i];
            cout << endl;
        }
    }
    return 0;
}

我的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

char s[50];

int main()
{
    while(scanf("%s", s)!=EOF)
    {
        vector<int> v;//存储二进制结果
        int st = 0, cnt, flag, len = strlen(s);
        while(st<len)
        {
            cnt = 0, flag = 0;//cnt上一位的余数,flag标记是否可以移动st
            for(int i=st; i<len; i++)
            {
                int a = (s[i]-'0'+cnt*10);//获取当前位
                cnt = a%2;//余数
                a /= 2;
                s[i] = a+'0';
                if(a!=0) flag = 1;
                else if(!flag) st++;
            }
            v.push_back(cnt);
        }
        for(int i=v.size()-1; i>=0; i--)
            printf("%d", v[i]);
        printf("\n");
    }
    return 0;
}
分解素因数
质数的个数

题目链接质因数的个数
题目大意:输入一个正整数N(1<N<10^9),输出质因数的个数。
思路:难点是正整数的取值范围太大,使用素数筛选法时无法完全存入数组。但明显知道,n只能有一个大于sqrt(n)的素因数,所以我们实际上只需要筛选前100000个素数即可。
求素因数的的方法是,遍历区间内的所有素数,得到满足为因数的素数,一直用该正整数除以该因数,直到无法整除,继续遍历循环。如果遍历完区间后,该正整数仍然不为1,则加一即可。

方法一:直接求个数
代码如下:

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

const int MAX = 100001;
vector<int> v;
int a[MAX];

void checkPrime()
{
    memset(a, 1, sizeof(a));
    v.clear();
    a[1] = 0;
    for(int i=2; i<MAX; i++)
    {
        if(a[i])
        {
            for(int j=2*i; j<MAX; j+=i)
                a[j] = 0;
            v.push_back(i);
        }
    }
}
int solve(int n)
{
    int i=0, sum = 0;
    while(i<v.size())
    {
        while(n%v[i]==0)
        {
            n/=v[i];
            sum++;
        }
        i++;
    }
    if(n!=1) sum++;
    return sum;
}
int main()
{
    int N;
    checkPrime();
    while(cin >> N)
    {
        cout << solve(N) << endl;
    }
    return 0;
}

方法二:保存质因数和其幂数。
代码如下:

#include <iostream>
using namespace std;

const int MAX = 100001;
bool mark[MAX];
int prime[MAX];
int primeSize;

void init()
{
    primeSize = 0;
    for(int i=2; i<MAX; i++)
    {
        if(mark[i]) continue;
        prime[primeSize++] = i;
        if(i>=1000) continue;
        for(int j=i*i; j<=MAX; j+=i)
            mark[j] = true;
    }
}
int main()
{
    init();
    int n;
    while(cin >> n)
    {
        int ansPrime[30];
        int ansSize = 0;
        int ansNum[30];
        for(int i=0; i<primeSize; i++)
        {
            if(n%prime[i]==0)
            {
                ansPrime[ansSize] = prime[i];
                ansNum[ansSize] = 0;
                while(n%prime[i]==0)
                {
                    ansNum[ansSize]++;
                    n/=prime[i];
                }
                ansSize++;
                if(n==1) break;
            }
        }
        if(n!=1)
        {
            ansPrime[ansSize] = n;
            ansNum[ansSize++] = 1;
        }
        int ans = 0;
        for(int i=0; i<ansSize; i++)
        {
            ans += ansNum[i];
        }
        cout << ans << endl;
    }
    return 0;
}
整除问题

题目链接整除问题
思路参考《王道计算机考研机试指南》第83页。
以下是我根据其思路写的代码,可以通过,不过比着书上的代码繁杂了一些。

  • 思路:根据因数分解定理,将n!和a分别分解为多个因数的乘积,然后比较因数的指数关系即可。
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;

const int MAX = 1001;
bool mark[MAX];
int prime[MAX];
int primeSize;

void init()
{
    primeSize = 0;
    memset(mark, false, sizeof(mark));
    for(int i=2; i<MAX; i++)
    {
        if(mark[i]) continue;
        prime[primeSize++] = i;
        //if(i>=1000) continue;
        for(int j=i*i; j<=MAX; j+=i)
            mark[j] = true;
    }
}

int main()
{
    int n, a;
    init();
    vector<int> ansPrimen, ansPrimea;
    vector<int> cntn, cnta;
    while(cin >> n >> a)
    {
        ansPrimen.clear();
        ansPrimea.clear();
        cntn.clear();
        cnta.clear();

        for(int i=0; i<primeSize; i++)
        {
            if(n<prime[i] && a==1) break;
            if(n>=prime[i])
            {
                int t = 1, sumn = 0;
                while(n/pow(prime[i], t))//求n!的素因数
                {
                    sumn += n/pow(prime[i], t);
                    t++;
                }
                ansPrimen.push_back(prime[i]);
                cntn.push_back(sumn);
            }
            if(a%prime[i]==0 && a!=1)//求a的素因数
            {
                int suma = 0;
                while(a%prime[i]==0)
                {
                    suma++;
                    a/=prime[i];
                }
                ansPrimea.push_back(prime[i]);
                cnta.push_back(suma);
            }
        }
        int j = 0, k = MAX;
        for(int i=0; i<ansPrimea.size(); i++)
        {
            while(ansPrimea[i]!=ansPrimen[j]) j++;
            k = min(k, cntn[j]/cnta[i]);
        }
        cout << k << endl;
    }
    return 0;
}

代码2:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;

const int MAX = 1005;
int isPrime[MAX], prime1[MAX], prime2[MAX], e1[MAX], e2[MAX];
void checkPrime()
{
    fill(isPrime, isPrime+MAX, 1);
    isPrime[0] = 0, isPrime[1] = 0;
    for(int i=2; i<=sqrt(MAX+0.5); i++)
    {
        if(!isPrime[i]) continue;
        for(int j=2*i; j<=MAX; j+=i)
        {
            isPrime[j] = 0;
        }
    }
}
int main()
{
    int n, a;
    checkPrime();
    scanf("%d%d", &n, &a);
    memset(e1, 0, sizeof(e1));
    memset(e2, 0, sizeof(e2));
    //获取n!的因数因数分解
    for(int i=2; i<=n; i++)
    {
        int cnt = i;
        if(isPrime[cnt])
        {
            e1[cnt]++;
            continue;
        }
        for(int j=2; j<=n; j++)
        {
            if(j>cnt) break;
            if(!isPrime[j]) continue;
            while(cnt%j==0)
            {
                cnt /= j;
                e1[j]++;
            }
        }
        if(cnt!=1) e1[cnt]++;
    }
    //获取a的因数分解
    int cnt = a;
    for(int j=2; j<=a; j++)
    {
        if(j>cnt) break;
        if(!isPrime[j]) continue;
        while(cnt%j==0)
        {
            cnt /= j;
            e2[j]++;
        }
    }
    if(cnt!=1) e2[cnt]++;
    //根据指数比较出k
    int k = 1<<29;
    for(int i=2; i<=a; i++)
    {
        if(!isPrime[i] || e2[i]==0) continue;
        if(e1[i]>=e2[i])//必须保证e2[i]!=0
        {
            k = min(k, e1[i]-e2[i]+1);
        }
        else//说明n!不是a的倍数
        {
            k = 0;
            break;
        }
    }
    printf("%d\n", k);
    return 0;
}
约数的个数

题目链接约数的个数
题目大意:给出正整数(1<n<10^9)的因数的个数。
难点是数的范围太大,不过由质数的个数一题的思想可以将其转化为sqrt(n)的范围。
代码如下:

#include <iostream>
#include <cmath>
using namespace std;

const int MAX = 10000001;
int a[MAX];

int main()
{
    int n;
    while(cin >> n && n)
    {
        for(int i=0; i<n; i++)
            cin >> a[i];
        for(int i=0; i<n; i++)
        {
            int sum = 0;
            if(a[i]==1)
            {
                cout << 1 << endl;
                continue;
            }
            int t = sqrt(a[i]);//默认向下取整
            if(t*t==a[i]) sum--;
            for(int j=1; j<=t; j++)
            {
                if(a[i]%j==0) sum+=2;
            }
            cout << sum << endl;
        }
    }
    return 0;
}
二分求幂
大数的二分求幂

题目链接人见人爱A^B
题目大意:求A^B(1<=A,B<=10000)
思路:可以跟二进制联系在一起,也可以跟奇偶性联系在一起。

代码如下

#include <iostream>
using namespace std;

typedef long long LL;
const int MAX = 1000;

LL Power(LL m, LL n)
{
    LL res = 1;
    while(n>0)
    {
        if(n&1) res = res*m%MAX;//n为奇数时将上一次计算的值乘起来
        m = m*m%MAX;
        n >>= 1;//n除以2
    }
    return res;
}
int main()
{
    LL m, n;
    while(cin >> m >> n && (m+n))
    {
        LL res = Power(m, n);
        cout << res << endl;
    }
    return 0;
}
矩阵快速幂

题目链接Tr A
题目大意:求矩阵A的迹的K此幂的迹元素之和。K(2 <= K < 10^9)
思路:此题为矩阵快速幂的模板题。
参考博客矩阵快速幂(原理+模板)
代码如下:

#include <iostream>
#include <cstring>
using namespace std;

typedef long long LL;
const int mod = 9973;
struct Mat//矩阵结构体
{
    int a[11][11];
};
Mat M;

Mat mul(Mat A, Mat B, LL m)//矩阵乘法
{
    Mat C;
    for(int i=0; i<m; i++)//初始化
    {
        for(int j=0; j<m; j++)
        {
            C.a[i][j] = 0;
        }
    }
    for(int i=0; i<m; i++)
    {
        for(int j=0; j<m; j++)
        {
            for(int k=0; k<m; k++)
            {
                C.a[i][j] = C.a[i][j]%mod+A.a[i][k]*B.a[k][j]%mod;
            }
        }
    }
    return C;
}

Mat Pow(Mat M, LL p, LL m)//矩阵快速幂模板
{
    Mat res;
    for(int i=0; i<m; i++)//初始化为单位矩阵,单位矩阵乘以任何矩阵不变
    {
        for(int j=0; j<m; j++)
        {
            if(i==j)
                res.a[i][j] = 1;
            else
                res.a[i][j] = 0;
        }
    }
    while(p)//类似二分求幂
    {
        if(p&1) res = mul(res, M, m);
        M = mul(M, M, m);
        p >>= 1;
    }
    return res;
}
int main()
{
    LL n, m, p;
    cin >> n;
    while(n--)
    {
        cin >> m >> p;
        for(int i=0; i<m; i++)
        {
            for(int j=0; j<m; j++)
            {
                cin >> M.a[i][j];
            }
        }
        Mat res = Pow(M, p, m);

        int sum = 0;
        for(int i=0; i<m; i++) sum += res.a[i][i];//求迹和
        cout << sum%mod << endl;
    }
    return 0;
}
高精度整数(较难)
a+b(简单模板)

题目链接a+b
方法一:建立一个大整数的结构体
代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX = 1001;
struct bigInteger
{
    int digit[MAX];
    int size;
    void init()
    {
        for(int i=0; i<MAX; i++) digit[i] = 0;
        size = 0;
    }
    void set(char str[])
    {
        init();
        int L = strlen(str);
        for(int i=L-1, j=0, t=0, c=1; i>=0; i--)//j控制没4个字符转换为一个数字存入数组,t临时保存字符转换为数字的中间值,c表示当前为的权重,按1,10,100,1000顺序变化
        {
            t += (str[i]-'0')*c;//计算这个四位数中当前字符代表的数字,即数字乘以当前位权重
            j++;//当前字符数增加
            c *= 10;//计算下一位权重
            if(j==4 || i==0)//若已经连续转换4个字符,或者已经到达最后一个字符
            {
                digit[size++] = t;
                j = 0;
                t = 0;
                c = 1;
            }
        }
    }
    void output()
    {
        for(int i=size-1; i>=0; i--)
        {
            if(i!=size-1) printf("%04d", digit[i]);
            else          printf("%d", digit[i]);
        }
        printf("\n");
    }
    bigInteger operator + (const bigInteger &A) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        for(int i=0; i<A.size || i<size; i++)
        {
            int tmp = A.digit[i]+digit[i]+carry;
            carry = tmp/10000;
            tmp %= 10000;
            ret.digit[ret.size++] = tmp;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }
}a, b, c;

int main()
{
    char str1[MAX], str2[MAX];
    while(scanf("%s%s", str1, str2)!=EOF)
    {
        a.set(str1);
        b.set(str2);
        c = a+b;
        c.output();
    }
    return 0;
}

方法二:自己写的,直接函数实现,不如方法一规范。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int MAX = 1001;
struct bigInteger
{
    int digit[MAX];
    int sized;
};

bigInteger str2num(char s[])
{

    bigInteger B;
    memset(B.digit, 0, sizeof(B.digit));
    int k = 0, i;
    for(i=strlen(s)-1; i>=0; i-=4)
    {
        if(i-3<0)
        {
            break;
        }
        for(int j=i-3; j<=i; j++)
        {
            B.digit[k] = B.digit[k]*10+s[j]-'0';
        }
        k++;
    }

    if(i>=0)
    {
        for(int j=0; j<=i; j++)
        {
            B.digit[k] = B.digit[k]*10+s[j]-'0';
        }
        k++;
    }
    B.sized = k;
    return B;
}

bigInteger add(bigInteger a, bigInteger b)
{
    int L = max(a.sized, b.sized), m = 0;
    if(a.sized<b.sized) swap(a, b);
    for(int i=0; i<L; i++)
    {
        int t = a.digit[i]+b.digit[i]+m;
        if(t<10000)
        {
            a.digit[i] = t;
            m = 0;
        }
        else
        {
            a.digit[i] = t%10000;
            m = 1;
        }
    }
    if(m)
    {
        a.digit[L] = a.digit[L]+1;
        a.sized++;
    }
    return a;
}

int Pow(int a, int b)
{
    int c = 1;
    for(int i=0; i<b; i++)
    {
        c *= a;
    }
    return c;
}
int main()
{
    char s1[MAX], s2[MAX];
    while(scanf("%s%s", s1, s2)!=EOF)
    {
        bigInteger a = str2num(s1);
        bigInteger b = str2num(s2);
        bigInteger c = add(a, b);

        for(int i=c.sized-1; i>=0; i--)
        {
            if(i==c.sized-1)
            {
                cout << c.digit[i];
                continue;
            }
            int k = 3, t = c.digit[i];
            while(k>=0)
            {
                cout << t/Pow(10, k);
                t = t%Pow(10, k);
                k--;
            }
        }
        cout << endl;
    }
    return 0;
}

方法三:(实用string的简单实现方法)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;

string add(string a, string b)
{
    string c = "";//存储结果
    int lena = a.size(), lenb = b.size();
    int cnta = lena-1, cntb = lenb-1, pre = 0;//cnta,cntb是下标,pre是进位
    int ai, bi, ci;
    while(cnta>=0 || cntb>=0)
    {
        if(cnta>=0) ai = a[cnta]-'0';
        else        ai = 0;
        if(cntb>=0) bi = b[cntb]-'0';
        else        bi = 0;
        ci = ai+bi+pre;
        pre = ci/10;
        ci = ci%10;
        c = char(ci+'0')+c;
        cnta--, cntb--;
    }
    if(pre) c = char(pre+'0')+c;//不要忘记最后检查一下
    return c;
}
int main()
{
    string a, b;
    while(cin >> a >> b)
    {
        cout << add(a, b) << endl;
    }
    return 0;
}
进制转换(综合题,难)

题目链接进制转换
手动进制转换,以10进制为中介。
代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int MAX = 1001;

struct bigInteger
{
    int digit[MAX];
    int size;

    void init()
    {
        memset(digit, 0, sizeof(digit));
        size = 0;
    }
    void set(char str[])
    {
        init();
        int L = strlen(str);
        for(int i=L-1, j=0, t=0, c = 1; i>=0; i--)
        {
            t += (str[i]-'0')*c;
            j++;
            c *= 10;
            if(j==4 || i==0)
            {
                digit[size++] = t;
                j = 0;
                t = 0;
                c = 1;
            }
        }
    }
    void output()
    {
        for(int i=size-1; i>=0; i--)
        {
            if(i!=size-1) printf("%04d", digit[i]);
            else          printf("%d", digit[i]);
        }
        printf("\n");
    }

    bigInteger operator * (const int &x) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        for(int i=0; i<size; i++)
        {
            int tmp = digit[i]*x+carry;
            ret.digit[ret.size++] = tmp%10000;
            carry = tmp/10000;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }

    bigInteger operator + (const bigInteger &A) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        for(int i=0; i<size; i++)
        {
            int tmp = digit[i]+A.digit[i]+carry;
            ret.digit[ret.size++] = tmp%10000;
            carry = tmp/10000;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }

    bigInteger operator / (const int &x) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0, flag = 0;
        for(int i=size-1; i>=0; i--)
        {
            ret.digit[i] = (digit[i]+carry*10000)/x;
            carry = (digit[i]+carry*10000)%x;
            if(ret.digit[i]!=0 && flag==0)
            {
                ret.size = i;
                flag = 1;
            }
        }
        ret.size++;
        return ret;
    }

    int operator % (const int &x) const
    {
        int carry = 0;
        for(int i=size-1; i>=0; i--)
        {
            carry = (digit[i]+carry*10000)%x;
        }
        return carry;
    }
};

char str[MAX], ans[MAX];
bigInteger a, b, c;

int main()
{
    int M, N, X;
    while(scanf("%d%d", &M, &N)!=EOF)
    {
        scanf("%s", str);
        a.set("0");
        b.set("1");
        int t;
        for(int i=strlen(str)-1; i>=0; i--)//得到转化为10进制的数字
        {
            if(str[i]>='0' && str[i]<='9') t = str[i]-'0';
            else                           t = str[i]-'A'+10;
            a = b*t+a;//注意没有定义符号的优先级和括号
            b = b*M;
        }
        int k = 0;
        if(N!=10)
        {
            do
            {
                //cout << a.size << endl;
                t = a%N;
                a = a/N;
                if(t>=10) ans[k++] = t-10+'a';
                else      ans[k++] = t+'0';
            }while(a.size!=1 || a.digit[0]!=0);
            for(int i=k-1; i>=0; i--)
            {
                cout << ans[i];
            }
            cout << endl;
        }
        else
            a.output();
    }
    return 0;
}
十进制VS二进制

题目链接10进制vs2进制
思路:结构体及其内置函数与上题进制转换一样,就是主函数操作不一样。均是手动进制转换。
代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int MAX = 1001;
const int M = 1<<29;
struct bigInteger
{
    int digit[MAX];
    int size;

    void init()
    {
        memset(digit, 0, sizeof(digit));
        size = 0;
    }
    void set(char str[])
    {
        init();
        int L = strlen(str);
        for(int i=L-1, j=0, t=0, c = 1; i>=0; i--)
        {
            t += (str[i]-'0')*c;
            j++;
            c *= 10;
            if(j==4 || i==0)
            {
                digit[size++] = t;
                j = 0;
                t = 0;
                c = 1;
            }
        }
    }
    void output()
    {
        for(int i=size-1; i>=0; i--)
        {
            if(i!=size-1) printf("%04d", digit[i]);
            else          printf("%d", digit[i]);
        }
        printf("\n");
    }

    bigInteger operator * (const int &x) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        if(x==0) return ret;//乘以0,则为0
        for(int i=0; i<size; i++)
        {
            int tmp = digit[i]*x+carry;
            ret.digit[ret.size++] = tmp%10000;
            carry = tmp/10000;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }

    bigInteger operator + (const bigInteger &A) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        int L = max(size, A.size);
        for(int i=0; i<L; i++)
        {
            //if(i==L-1 && digit[i]==0 && A.digit[i]==0) break;
            int tmp = digit[i]+A.digit[i]+carry;
            ret.digit[ret.size++] = tmp%10000;
            carry = tmp/10000;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }

    bigInteger operator / (const int &x) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0, flag = 0;
        for(int i=size-1; i>=0; i--)
        {
            ret.digit[i] = (digit[i]+carry*10000)/x;
            carry = (digit[i]+carry*10000)%x;
            if(ret.digit[i]!=0 && flag==0)
            {
                ret.size = i;
                flag = 1;
            }
        }
        ret.size++;
        return ret;
    }

    int operator % (const int &x) const
    {
        int carry = 0;
        for(int i=size-1; i>=0; i--)
        {
            carry = (digit[i]+carry*10000)%x;
        }
        return carry;
    }
};

char str[MAX], ans[M];
bigInteger a, b, c;

int main()
{
    while(scanf("%s", str)!=EOF)
    {
        a.set(str);
        //a.output();
        b.set("0");
        c.set("1");
        int t, k = 0;
        while(a.size!=1 || a.digit[0]!=0)//a的值为0时跳出
        {
            t = a%2;
            a = a/2;
            ans[k++] = t+'0';
        }
        for(int i=k-1; i>=0; i--)
        {
            t = ans[i]-'0';
            b = c*t+b;
            c = c*2;
        }
        b.output();
    }
    return 0;
}
浮点加法

题目链接浮点加法
需要考虑整数和小数两种大整数的运算。

方法一:思想是分成两部分(整数和小数)相加,然后其中整数部分数组低下标处为低位,小数部分低下标处为高位。先算小数相加(有向整数的进位),从高下标向低下标遍历相加,整数部分相反方向遍历相加,其中有变量存着进位。
参考博客浮点数加法 九度oj
代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define LEN 101
 
int ia[LEN], fa[LEN], ib[LEN], fb[LEN], ic[LEN], fc[LEN];
 
int main()
{
    char str1[LEN], str2[LEN];
    int i, j, k, n, l1, l2, lai, laf, lbi, lbf, temp, flmax, ilmax;
 
    while (scanf("%d", &n) != EOF) {
        while (n --) {
            // 初始化
            memset(ia, 0, sizeof(ia));
            memset(fa, 0, sizeof(fa));
            memset(ib, 0, sizeof(ib));
            memset(fb, 0, sizeof(fb));
            memset(ic, 0, sizeof(ic));
            memset(fc, 0, sizeof(fc));
 
            // 接收第一个浮点数
            scanf("%s", str1);
            l1 = strlen(str1);
 
            // 构建整数部分数组
            for (i = 0; i < l1 && str1[i] != '.'; i ++) {
                ia[i] = str1[i] - '0';
            }
            lai = i;
 
            // 数位替换
            for (j = 0, k = lai - 1; j <= lai / 2 && j < k; j ++, k --) {
                temp = ia[j];
                ia[j] = ia[k];
                ia[k] = temp;
            }
 
            // 构建小数部分数组
            for (i += 1; i < l1; i ++) {
                fa[i - 1 - lai] = str1[i] - '0';
            }
            laf = i - 1 - lai;
 
 
            // 接收第二个浮点数
            scanf("%s", str2);
            l2 = strlen(str2);
 
            // 构建整数部分数组
            for (i = 0; i < l2 && str2[i] != '.'; i ++) {
                ib[i] = str2[i] - '0';
            }
            lbi = i;
 
            // 数位替换
            for (j = 0, k = lbi - 1; j <= lbi / 2 && j < k; j ++, k --) {
                temp = ib[j];
                ib[j] = ib[k];
                ib[k] = temp;
            }
 
            // 构建小数部分数组
            for (i += 1; i < l2; i ++) {
                fb[i - 1 - lbi] = str2[i] - '0';
            }
            lbf = i - 1 - lbi;
 
            // 谁的小数位数更多
            flmax = (laf >= lbf) ? laf : lbf;
            int c = 0;  //小数进位
            for (i = 0, j = flmax - 1; j >= 0; j --, i ++) {
                fc[i] = fa[j] + fb[j] + c;
                if (fc[i] >= 10) {
                    c = fc[i] / 10;
                    fc[i] %= 10;
                }else {
                    c = 0;
                }
            }
 
            // 整数相加
            ilmax = (lai >= lbi) ? lai : lbi;
            for (i = 0; i < ilmax; i ++) {
                ic[i] = ia[i] + ib[i] + c;
                if (ic[i] >= 10) {
                    c = ic[i] / 10;
                    ic[i] %= 10;
                }else {
                    c = 0;
                }
            }
            while (c) {
                ic[ilmax ++] = c % 10;
                c /= 10;
            }
 
 
            // 打印最后结果
 
            // 找到第一个不为0的整数位
            for (j = ilmax - 1; j >= 0; j --) {
                if (ic[j] != 0) {
                    break;
                }
            }
            if (j >= 0) {
                for (i = j; i >= 0; i --) {
                    printf("%d", ic[i]);
                }
            }else {
                printf("0");
            }
            printf(".");
 
            // 找到最后一个不为0的小数位
            for (j = 0; j < flmax - 1; j ++) {
                if (fc[j] != 0) {
                    break;
                }
            }
            for (i = flmax - 1; i >= j; i --) {
                printf("%d", fc[i]);
            }
            printf("\n");
 
            // 接收空行
            getchar();
        }
 
    }
 
    return 0;
}
/**************************************************************
    Problem: 1137
    User: wangzhengyi
    Language: C
    Result: Accepted
    Time:290 ms
    Memory:912 kb
****************************************************************/

方法二:
自己写的,按照给出的bigInteger结构体,将小数和整数部分均用结构体存储,然后操作的时候再手动浮点相加,写的不太好,但写了4、5个小时,总算过了,记录一下吧。
代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int MAX = 1001;
char s1[MAX], s2[MAX];

struct bigInteger
{
    int digit[MAX];
    int size;

    void init()
    {
        memset(digit, 0, sizeof(digit));
        size = 0;
    }
    void set(char str[])
    {
        init();
        int L = strlen(str);
        for(int i=L-1, j=0, t=0, c = 1; i>=0; i--)
        {
            t += (str[i]-'0')*c;
            j++;
            c *= 10;
            if(j==4 || i==0)
            {
                digit[size++] = t;
                j = 0;
                t = 0;
                c = 1;
            }
        }
    }
    void output()
    {
        for(int i=size-1; i>=0; i--)
        {
            if(i!=size-1) printf("%04d", digit[i]);
            else          printf("%d", digit[i]);
        }
        //printf("\n");
    }

    bigInteger operator + (const bigInteger &A) const
    {
        bigInteger ret;
        ret.init();
        int carry = 0;
        for(int i=0; i<A.size || i<size; i++)
        {
            int tmp = A.digit[i]+digit[i]+carry;
            carry = tmp/10000;
            tmp %= 10000;
            ret.digit[ret.size++] = tmp;
        }
        if(carry!=0) ret.digit[ret.size++] = carry;
        return ret;
    }
};
struct Node
{
    char str[2][MAX];
};
struct bigFloat
{
    bigInteger F[2];
    void init()
    {
        F[0].init();
        F[1].init();
    }
};
Node split(char s[], char t)
{
    //memset(str1, "", sizeof(str1));
    //memset(str2, "", sizeof(str2));
    Node S;
    memset(S.str, 0, sizeof(S.str));
    //S.init();
    int flag = 0, L = strlen(s), ind = 0;
    //cout << L << endl;
    for(int i=0; i<L; i++)
    {
        if(s[i]==t)
        {
            flag = 1;
            ind = i;
            continue;
        }
        if(flag)
        {
            S.str[1][i-ind-1] = s[i];
        }
        else
        {
            S.str[0][i] = s[i];
        }
    }
    return S;
}
int len(bigInteger a)
{
    int L = (a.size-1)*4;
    int t = a.digit[a.size-1];
    while(t)
    {
        t /= 10;
        L++;
    }
    return L;
}
int main()
{
    Node str1, str2;
    bigFloat f1, f2;
    bigInteger inter, floa, carry;
    while(scanf("%s%s", s1, s2)!=EOF)
    {
         inter.init();
         floa.init();
         carry.init();
         f1.init();
         f2.init();
         str1 = split(s1, '.');
         str2 = split(s2, '.');

         while(strlen(str2.str[1])<strlen(str1.str[1]))
         {
             strcat(str2.str[1], "0");
         }
          while(strlen(str2.str[1])>strlen(str1.str[1]))
         {
             strcat(str1.str[1], "0");
         }

         f1.F[0].set(str1.str[0]);
         f1.F[1].set(str1.str[1]);
         f2.F[0].set(str2.str[0]);
         f2.F[1].set(str2.str[1]);

         floa = f1.F[1]+f2.F[1];
         char p[MAX] = {'1'};
         int L1 = max(len(f1.F[1]), len(f2.F[1]));
         if(len(floa)>L1)
         {
             //cout << endl;
             if(len(floa)%4==1) floa.size--;
             else
             {
                 int t = floa.digit[floa.size-1], L = 1;
                 while(t)
                 {
                     t/=10;
                     L *= 10;
                 }
                 L /= 10;
                 floa.digit[floa.size-1] %= L;
             }
             //sprintf(p, "%d", t);
             carry.set(p);
         }
         inter = f1.F[0]+f2.F[0];
         inter = inter+carry;
         inter.output();
         printf(".");
         if(len(floa)<L1) cout << 0;
         floa.output();
         //cout << str1.str[0][0] << endl;
    }
    return 0;
}

后来我变强一些之后又写了一次,用例大概半个小时调试AC了,方法更精炼一些。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;

string add(string a, string b, int p)
{
    string c = "";//存储结果
    int lena = a.size(), lenb = b.size();
    int cnta = lena-1, cntb = lenb-1, pre = p;//cnta,cntb是下标,pre是进位
    int ai, bi, ci;
    while(cnta>=0 || cntb>=0)
    {
        if(cnta>=0) ai = a[cnta]-'0';
        else        ai = 0;
        if(cntb>=0) bi = b[cntb]-'0';
        else        bi = 0;
        ci = ai+bi+pre;
        pre = ci/10;
        ci = ci%10;
        c = char(ci+'0')+c;
        cnta--, cntb--;
    }
    if(pre) c = char(pre+'0')+c;//不要忘记最后检查一下
    return c;
}
string add2(string a, string b, int p)
{
    string c = "";//存储结果
    if(a.size()<b.size()) swap(a, b);
    int lena = a.size(), lenb = b.size();
    for(int i=lena-1; i>=lenb; i--)//实现将超出的位赋值
        c = a[i]+c;
    int cnta = lenb-1, cntb = lenb-1, pre = p;//cnta,cntb是下标,pre是进位
    int ai, bi, ci;
    while(cnta>=0 || cntb>=0)
    {
        if(cnta>=0) ai = a[cnta]-'0';
        else        ai = 0;
        if(cntb>=0) bi = b[cntb]-'0';
        else        bi = 0;
        ci = ai+bi+pre;
        pre = ci/10;
        ci = ci%10;
        c = char(ci+'0')+c;
        cnta--, cntb--;
    }
    if(pre) c = char(pre+'0')+c;//不要忘记最后检查一下
    return c;
}
int main()
{
    string a, b, a1, a2, b1, b2;
    while(cin >> a >> b)
    {
        int p = 0;
        int pa = a.find('.', 0);
        int pb = b.find('.', 0);
        if(pa<a.size() && pa>=0)//有小数位
            a1 = a.substr(0, pa), a2 = a.substr(pa+1, a.size()-pa);
        else//没有小数位
            a1 = a, a2 = "0";
        if(pb<b.size() && pb>=0)
            b1 = b.substr(0, pb), b2 = b.substr(pb+1, b.size()-pb);
        else
            b1 = b, b2 = "0";
        string c2 = add2(a2, b2, p);//小数位相加
        if(c2.size()>max(a2.size(), b2.size()))//如果小数位向整数位有进位
        {
            p = 1;
            c2.erase(0, 1);
        }
        string c1 = add(a1, b1, p);//整数位相加
        if(c2=="0")//如果结果没有小数位
            cout << c1 << endl;
        else
            cout << c1 << "." << c2 << endl;
    }
    return 0;
}
大整数排序

题目链接大整数排序
思路:自定义大整数的小于<符号,然后实现一个冒泡排序的函数即可。
PS:发现我之前的思路好傻,直接使用string字符串模拟大整数就可以了(string类型自带比较级),只不过当长度不同时,长度长的一定大,长度相同时,比较字符串大小即可。

string方法的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

struct BigInter
{
    string s;
    bool operator < (const BigInter &A) const
    {
        if(A.s.size()!=s.size())
            return s.size()<A.s.size();
        else
            return s<A.s;
    }
};
BigInter B[105];
int main()
{
    int N;
    cin >> N;
    for(int i=0; i<N; i++)
        cin >> B[i].s;
    sort(B, B+N);
    for(int i=0; i<N; i++)
        cout << B[i].s << endl;
    return 0;
}

代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int MAX = 1001;

struct bigInteger
{
    int digit[MAX];
    int size;

    void init()
    {
        memset(digit, 0, sizeof(digit));
        size = 0;
    }
    void set(char str[])
    {
        init();
        int L = strlen(str);
        for(int i=L-1, j=0, t=0, c = 1; i>=0; i--)
        {
            t += (str[i]-'0')*c;
            j++;
            c *= 10;
            if(j==4 || i==0)
            {
                digit[size++] = t;
                j = 0;
                t = 0;
                c = 1;
            }
        }
    }
    void output()
    {
        for(int i=size-1; i>=0; i--)
        {
            if(i!=size-1) printf("%04d", digit[i]);
            else          printf("%d", digit[i]);
        }
        printf("\n");
    }

    bool operator < (const bigInteger &x) const//自定义小于符号
    {
         //cout << size << " " << x.size << endl;
         //cout << size-x.size << endl;
         if(size-x.size==0)
         {
             //cout << 4 << endl;
             for(int i=size-1; i>=0; i--)
             {
                 if(digit[i]!=x.digit[i])
                    return digit[i]<x.digit[i];
             }
         }
         else
            return size<x.size;
         //cout << 3 << endl;
         return 0;
    }
};

void bigIntegerSort(bigInteger ret[], int n)
{

    for(int i=n-1; i>=0; i--)//简单的冒泡排序
    {
        for(int j=n-1; j>=n-i; j--)
        {
            if(ret[j]<ret[j-1])
            {
                bigInteger tmp = ret[j];
                ret[j] = ret[j-1];
                ret[j-1] = tmp;
            }
        }
    }
    //cout << endl;
    for(int i=0; i<n; i++)
    {
        ret[i].output();
    }
}

int main()
{
    int N;
    bigInteger ret[101];
    char str[MAX];
    while(cin >> N)
    {
        for(int i=0; i<N; i++)
        {
            scanf("%s", str);
            ret[i].init();
            ret[i].set(str);
            //ret[i].output();
        }
        bigIntegerSort(ret, N);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值