跳转至

NOIP2011普及组初赛 完善程序 第2题《大整数》

Keywords: 高精度计算

img

分析:鉴于题目过于长,140行代码,不截图放原题了,直接贴上代码和注释。

#include <cstring>
#include <iostream>

using namespace std;

const int N = 200;

struct hugeint
{
    int len, num[N];
};

hugeint times(hugeint a, hugeint b)  // ab乘积 
{
    int i, j;
    hugeint ans;                     //ans存放乘积 
    memset(ans.num, 0, sizeof ans.num);

    for (i = 1; i <= a.len; i++)
        for (j = 1; j <= b.len; j++)
            ans.num[i + j - 1] += a.num[i] * b.num[j];  // 高精度乘法  1空 

    for (i = 1; i <= a.len + b.len; i++)
    {
        ans.num[i+1] += ans.num[i] / 10;  //处理乘法进位 
        ans.num[i] %= 10;                 // 2空 
    } 

    //判断乘积是否比ab中的最大数,还要多1位   123 * 3 = 369 (位数 3 + 1 - 1)
    //789 * 3 = 2367 (位数 3 + 1)
    if (ans.num[a.len + b.len] > 0) ans.len = a.len + b.len;
    else ans.len = a.len + b.len - 1;  

    return ans;   
}

hugeint add(hugeint a, hugeint b)  // ab和 
{
    int i;
    hugeint ans;
    memset(ans.num, 0, sizeof ans.num);

    if (a.len > b.len) ans.len = a.len;  //看哪个位数长,用在ans 
    else ans.len = b.len;

    for (i = 1; i <= ans.len; i++)
    {
        ans.num[i] += a.num[i] + b.num[i];  //3空
        ans.num[i + 1] += ans.num[i] / 10;  //处理进位问题 
        ans.num[i] %= 10;                   //这个地方其实也是一个第2空的参考,可以仿照写 
    } 

    if (ans.num[ans.len + 1] > 0) ans.len++;  //最后相加后,多了一位 

    return ans;
}

hugeint average(hugeint a, hugeint b) // 求ab平均数的整数部分 求middle 
{
    int i;
    hugeint ans;
    ans = add(a, b);                 //ans存ab的和 

    for (i = ans.len; i >= 2; i--)
    {
        ans.num[i - 1] += (ans.num[i] % 2) * 10;         //从高位开始模拟除以2的动作  // 4空
        ans.num[i] /= 2;                                 //高一位除2的余数,*10加给下一位 
    }

    ans.num[1] /= 2;
    if (ans.num[ans.len] == 0) ans.len--;

    return ans;
}

hugeint plustwo(hugeint a)  // a加2之后的结果   
{
    int i;
    hugeint ans;
    ans = a;
    ans.num[1] += 2;   //num[1]是最后一位  倒序存的 

    i = 1; 
    while ((i <= ans.len) && (ans.num[i] >= 10))  //从最后一位往前捋 看有无进位
    {
        ans.num[i + 1] += ans.num[i] / 10;
        ans.num[i] %= 10;
        i++;
    }

    if (ans.num[ans.len + 1] > 0) ans.len++;      // 5空  加2后总位数多了一位

    return ans; 
} 

bool over(hugeint a, hugeint b) //a是否大于b 
{
    int i;
    if (a.len < b.len) return false;  //用数的位数长短来区分大小 
    if (a.len > b.len) return true;

    for (i = a.len; i >= 1; i--)       //当位数相同时,从高位开始逐位比较 
    {
        if (a.num[i] < b.num[i]) return false;
        if (a.num[i] > b.num[i]) return true;
    }

    return false;
}

int main()
{
    int i;
    string s;
    cin >> s;
    hugeint target, left, middle, right;
    memset(target.num, 0, sizeof target.num);

    //初始化数组,倒序,存放n的每一位 
    target.len = s.length();
    for (i = 1; i <= target.len; i++)            //下标从1开始 
        target.num[i] = s[target.len - i] - '0'; // s字符数组,转化成数组,倒序存入   7空 

    //二分法,求mid 
    memset(left.num, 0, sizeof left.num);
    left.len = 1, left.num[1] = 1, right = target;
    do
    {
        middle = average(left, right); 
        if (over(times(middle, middle), target))   // check middle的平方是否大于target 8空 
            right = middle;                        // 大于 就是middle取大了 
        else
            left = middle;
    }
    while (!over(plustwo(left), right));   //left右移两步,还是小于right,就继续while循环 

    //输出结果 
    for (i = left.len; i >= 1; i--) cout << left.num[i];  //正序输出 

    return 0;
}