Codechef练习问题需要帮助 - 在阶乘中找到尾随零

时间:2010-04-04 18:03:29

标签: c++ algorithm

我现在已经24小时工作,试图优化它。问题是如何在大约8秒内找到10000000和1000万个测试用例中的数字因子的尾随零数。

代码如下:

#include<iostream>

using namespace std;

int count5(int a){
    int b=0;
    for(int i=a;i>0;i=i/5){
        if(i%15625==0){
            b=b+6;
            i=i/15625;
        }
        if(i%3125==0){
            b=b+5;
            i=i/3125;
        }
        if(i%625==0){
            b=b+4;
            i=i/625;
        }
        if(i%125==0){
            b=b+3;
            i=i/125;
        }
        if(i%25==0){
            b=b+2;
            i=i/25;
        }
        if(i%5==0){
            b++;
        }
        else
            break;

    }
    return b;
}
int main(){
    int l;
    int n=0;
    cin>>l; //no of test cases taken as input
    int *T = new int[l];

    for(int i=0;i<l;i++)
        cin>>T[i]; //nos taken as input for the same no of test cases


    for(int i=0;i<l;i++){
        n=0;
        for(int j=5;j<=T[i];j=j+5){
            n+=count5(j); //no of trailing zeroes calculted 
        }
        cout<<n<<endl; //no for each trialing zero printed
    }

    delete []T;


}   

请通过建议新方法或建议对此方法进行一些修改来帮助我。

7 个答案:

答案 0 :(得分:4)

使用以下定理:

  

如果p是素数,那么最高   分割n的p的力量! (N   factorial)是[n / p] + [n / p ^ 2] +   [n / p ^ 3] + ... + [n / p ^ k],其中k是   p <= n的最大功率,[x]是x的整数部分。

参考:http://planetmath.org/encyclopedia/PrimePowerDividingAFactorial.html

答案 1 :(得分:4)

最佳解决方案在O(log N)时间运行,其中N是您要为其找到零的数字。使用此公式:

Zeroes(N!) = N / 5 + N / 25 + N / 125 + ... + N / 5^k,直到除法变为0.您可以在wikipedia上阅读更多内容。

例如,在C中,这将是:

int Zeroes(int N)
{
    int ret = 0;
    while ( N )
    {
        ret += N / 5;
        N /= 5;
    }
    return ret;
}

这将在足够快的计算机上以8秒的速度运行。你可以通过使用查找表加快速度,虽然我不确定你有多少内存可用。

这是另一个建议:不存储数字,你不需要它们!读取时计算每个数字的零数。

如果这是针对在线评判的,根据我的经验,在线评委夸大了问题的时间限制,所以即使你有正确的算法,你也不得不诉诸丑陋的黑客。一个这样丑陋的黑客就是不使用诸如cinscanf之类的函数,而是使用fread在char数组中一次读取一堆数据,然后解析该数据(DON '虽然使用sscanf或stringstreams)并从中获取数字。丑陋,但通常更快。

答案 2 :(得分:1)

这个问题来自codechef。

http://www.codechef.com/problems/FCTRL

这个解决方案怎么样:

#include <stdio.h>

int a[] = {5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625};

int main()
{
    int i, j, l, n, ret = 0, z;
    scanf("%d", &z);
    for(i = 0; i < z; i++)
    {
        ret = 0;
        scanf("%d", &n);
        for(j = 0; j < 12; j++)
        {
            l = n / a[j];
            if(l <= 0)
                break;
            ret += l;
        }
        printf("%d\n", ret);
    }
    return 0;
}

任何优化???

答案 3 :(得分:1)

知道这已经超过2年了,但这是我的代码供将来参考:

#include <cmath>
#include <cstdio>

inline int read()
{
    char temp;
    int x=0;
    temp=getchar_unlocked();
    while(temp<48)temp=getchar_unlocked();
    x+=(temp-'0');
    temp=getchar_unlocked();
    while(temp>=48)
    {
        x=x*10;
        x+=(temp-'0');
        temp=getchar_unlocked();
    }
    return x;
}
int main()
{
    int T,x,z;
    int pows[]={5,25,125,625,3125,15625,78125,390625,1953125,9765625,48828125,244140625};
    T=read();
    for(int i=0;i<T;i++)
    {
        x=read();
        z=0;
        for(int j=0;j<12 && pows[j]<=x;j++)
            z+=x/pows[j];
        printf("%d\n",z);
    }
    return 0;
} 

它以0.13秒的速度运行

答案 4 :(得分:1)

这是我接受的解决方案。它的得分为1.51s,2.6M。不是最好的,但也许它可以帮助你。

#include <iostream>
using namespace std;

void calculateTrailingZerosOfFactoriel(int testNumber)
{

    int numberOfZeros = 0;
    while (true) 
    {

        testNumber = testNumber / 5;
        if (testNumber > 0) 
            numberOfZeros += testNumber;
        else
            break;
    }
    cout << numberOfZeros << endl;
}

int main() 
{

    //cout << "Enter number of tests: " << endl;
    int t;
    cin >> t;

    for (int i = 0; i < t; i++) 
    {
        int testNumber;
        cin >> testNumber;
        calculateTrailingZerosOfFactoriel(testNumber);
    }
    return 0;
}

答案 5 :(得分:-1)

#include <cstdio>

int main(void) {
    long long int t, n, s, i, j;
    scanf("%lld", &t);
    while (t--) {
        i=1; s=0; j=5;
        scanf("%lld", &n);
        while (i != 0) {
            i = n / j;
            s = s + i * (2*j + (i-1) * j) / 2;
            j = j * 5; 
        }
        printf("%lld\n", s);
    }
    return 0;
}

答案 6 :(得分:-2)

您显然已经知道正确的算法。代码中的瓶颈是使用cin / cout。处理非常大的输入时,与scanf相比,cin非常慢。

scanf也比读取输入的直接方法(如fread)慢,但使用scanf足以解决在线评委的几乎所有问题。

详细内容见Codechef FAQ,这可能值得先读一读;)