CatCoding

一个小题目

2010-08-02

前些天在班级群里看到一个笔试题:

从 1 到 100000 中任意拿掉两个数字,把剩下的 99998 个数顺序打乱,并且放入数组 A 中。要求只扫描一遍,把这两个数找出来;可以使用最多不超过 5 个局部变量,不能使用数组变量,并且不能改变原数组的值。


也想不到什么更好的解法,原解法是顺序扫一边求得所有数的乘积 (mul_res)、和 (sum_res)。用 (N!)/mul_res 得到两个数的乘积,1 到 100000 的和减去 sum_res 得到两个数之和。解这个方程得到两个数。关键是 N!太大了,C 会溢出。刚开始想想乘积每次模 100000,后来写了一下还是不对的,因为模 100000 中可能就出现了 0,后面全为 0 了。最后想到这么一个办法,不过中间 除法和比较多。也许有更快的解法。

//1 到 100 000
#include <iostream>
#include <math.h>

using namespace std;
#define N 100000
typedef long long LL;

LL a;
LL b;
LL vec[N];
int cnt;
LL  MAX_MUL;

void Find(const LL* vec)
{
    int sum=0;
    LL  mul=1;
    LL  Now=1;
    for(int i=0;i<cnt;i++)
    {
        sum+=vec[i];
        while(mul%vec[i]!=0)
            mul*=(++Now);
        mul/=vec[i];
    }
    while(Now<100000)
         mul*=(++Now);
    LL diff=((1+N)*N)/2-sum;
    cout<<diff<<" "<<mul<<endl;
    LL a=(diff+sqrt(diff*diff-4*mul))/2;
    cout<<a<<" "<<diff-a<<endl;
}

int main()
{
    srand(time(NULL));
    a=(rand()%100000)+1;
    b=(rand()%100000)+1;
    cnt=0;
    for(int i=1;i<=N;i++)
    {
        if(i!=a&&i!=b)
            vec[cnt++]=i;
    }
    cout<<a<<" "<<b<<" "<<endl;
    cout<<a+b<<" "<<a*b<<endl;
    Find(vec);
}

经熊师兄指点,上面的解法还是不对,如果 vec 前面刚好为比较大的素数,mul 就溢出了。正确的解法应该为求 x+y=B, x^2+y^2=A, 1-100000 的平方和可以用 double 存下来,然后减去 vec 里面的平方和就得到 x^2+y^2 的值。

void Find(const LL* vec)
{
    double sum=0;
    double  square_sum=0;
    for(int i=0;i<cnt;i++)
    {
        sum+=vec[i];
        square_sum+=(vec[i]*vec[i]);
    }
    double diff=((1+N)*N)/2-sum;
    double square_sum_diff=
        ((double)N*(N+1)*(2*(double)N+1))/6 - square_sum;
    cout<<diff<<" "<<square_sum_diff<<endl;
    a=(2*(diff)+sqrt(8*square_sum_diff-4*diff*diff))/4;
    cout<<a<<" "<<diff-a<<endl;
}

公号同步更新,欢迎关注👻