回 帖 发 新 帖 刷新版面

主题:第38次编程比赛第一题题目

在N以内(小于等于N)找出一个数,要求:
   1.这个数的约数的个数达到最大,
   2.如果有好几个数满足条件1,仅取最小的那个数

说明:
 1) 1<N<=[color=FF0000]2^32-1[/color],每个N的时限是1000ms。内存限制256M,注意使用适当数据类型,以免溢出。

函数原型:
// n: 范围
// result:结果,存放符合条件的那个数
// count:存入存放符合条件的那个数的约数的个数
// arr:存放那个数的所有约数,按照从小到大的顺序
void Search(unsigned long n, unsigned long *result,int *count,unsigned long arr[]);

例:
n=100, 则 result=60,
在100以内,60和90的约数个数同为12个,达到这个范围内所有整数中,其约数个数的最大值,但60比90小,所有正确答案为60。60共有12个约数:1,2,3,4,5,6,10.12.15.20.30,60.所以count应该存入12,从arr[0]开始,应该依次写入1,2,3,4,5,6,10.12.15.20.30,60。

回复列表 (共47个回复)

11 楼

// 统计n的约数个数,并得到n的质因子
// factors[]--n的质因子
// factorCount[]-- 每个质因子的数量
// numFactors -- n的质因子个数
int countDivisor(long n, long factors[], long factorCount[], long &numFactors)
{
    int        nCount = 1;
    int        temp = 0;

    if(n <= 1)
        return nCount;

    while((n&1) == 0)
    {
        n >>= 1;
        ++temp;
    }

    nCount *= temp + 1;
    numFactors = 0;
    if(temp > 0)
    {
        factors[numFactors] = 2;    // 存放具体的素因子
        factorCount[numFactors++] = temp;    // 存放每个素因子的个数
    }

    int        nSqrt = (int)sqrt((double)n+0.1);
    long    nPrimesCount = 0;
    long    *pPrimes = new long[nSqrt+1];
    assert(pPrimes != NULL);
    nPrimesCount = GetPrimes(nSqrt, pPrimes);

    bool    bIsPrime = true;    // 判断n是否素数
    for(int i=1; i<nPrimesCount; ++i)
    {
        if(n < pPrimes[i])
        {
            break;
        }
        temp = 0;
        while(n%pPrimes[i] == 0)
        {
            ++temp;
            n /= pPrimes[i];
            bIsPrime = false;
        }
        nCount *= temp + 1;
        if(temp > 0)
        {
            factors[numFactors] = pPrimes[i];
            factorCount[numFactors++] = temp;
        }
    }

    if(n > 1)
    {
        nCount *= 2;
        factors[numFactors] = n;
        factorCount[numFactors++] = 1;
    }

    delete []pPrimes;

    if(nCount == 1 && bIsPrime == true)
        nCount = 2;

    return nCount;
}

12 楼

// 根据某个数的质因子分解结果产生所有的约数
// *result -- 存放约数
// count -- 统计约数个数
// factors[]--质因子
// factorCount[]-- 每个质因子的数量
// numFactors -- 质因子个数
// value -- 约数(用于计算约数)
void GenerateDivisors(unsigned long *result, int &count, long factors[], long factorCount[], long numFactors, long &value)
{
    long        i;
    long        oldvalue;

    if(numFactors <= 0)
    {
        result[count++] = value;
        return;
    }

    oldvalue = value;
    GenerateDivisors(result, count, factors, factorCount, --numFactors, value);
    value = oldvalue;
    for(i=0; i<factorCount[numFactors]; i++)
    {
        value *= factors[numFactors];
        oldvalue = value;
        GenerateDivisors(result, count, factors, factorCount, numFactors, value);
        value = oldvalue;
    }
}

// n: 范围
// result:结果,存放符合条件的那个数
// count:存入存放符合条件的那个数的约数的个数
// arr:存放那个数的所有约数,按照从小到大的顺序
void Search(unsigned long n, unsigned long *result,int *count,unsigned long arr[])
{
    unsigned long        i;

    assert(n >= 1);

    if(n == 1)
    {
        *result = 1;
        *count  = 1;
        arr[0]  = 1;
        return ;
    }

    int        temp;
    long    factors[20];        // 存储n的质因子    
    long    factorCount[20];    // 每个质因子的个数
    long    numFactors = 0;        // n的质因子个数
    long    value;
    int        step;                // 循环步长(目的:提高速度)

    unsigned long    start;
    if(n < 12)
        step = 2;
    else if(n < 120)
        step = 12;
    else if(n < 1680)
        step = 60;
    else if(n < 2520)
        step = 120;
    else if(n < 55440)
        step = 2520;
    else if(n < 720720)
        step = 27720;
    else if(n < 61261200)
        step = 360360;
    else
        step = 6126120;

    start = 0;
    while(start <= n)
        start += step;
    start -= step;

    *count  = 0;
    *result = 0;
    for(i=start; i>=n/2; i-=step)
    {
        temp = countDivisor(i, factors, factorCount, numFactors);
        if(temp >= *count)
        {
            *count = temp;
            *result = i;
            temp  = 0;
            value = 1;
            // 产生该数的所有约数
            GenerateDivisors(arr, temp, factors, factorCount, numFactors, value);
        }
    }

    qsort((void*)arr, *count, sizeof(unsigned long), cmp);
}

13 楼

//编译器Dev-C++  
//计算n的公约数数目 
int ComputeNum(unsigned long n)    
{
    unsigned long  big=n, small=0;
    int count = 2;
    
    for( small = 2; small < big; small++)    
    {
        if(!(n%small))
        {
            big = n/small;
            count += small == big ? 1: 2;
        }    
    } 
    return count;
}

void AddArray(unsigned long n, int count, unsigned long arr[])
{
    unsigned long big = n, small = 0;  
    int i=0, num = count - 1;
    arr[i++] = 1;
    arr[num--] = n;
    
    for( small = 2; small < big; small++)
    {
        if(!(n%small))
        {
            big = n/small;  
            arr[i++] = small;
            if( small != big )
                arr[num--] = big;
        }
    }      
}

void Search(unsigned long n, unsigned long *result, int *count, unsigned long arr[])
{
    unsigned long i, k;
    unsigned long m = (unsigned long)(n/2);
    int temp, length;
    
    *count = 2;
    *result = 2;
    
    length = n > 100 ? 10: 2;
    k = n > 100 ? (unsigned long)(n/10)*10: (n%2? n-1: n);
    
    for( i = k; i > m; i-=length)
    {        
        if((temp=ComputeNum(i)) >= *count)
        {
            *count = temp;
            *result = i;    
        }           
    }
    AddArray(*result, *count, arr);
}

14 楼

// 重新修改了下,速度有了大幅度提高
// vc6下编译通过
int cmp(const void*x, const void *y)
{
    return (*(int*)x - *(int*)y);
}

// 统计n的约数个数,并得到n的质因子
// factors[]--n的质因子
// factorCount[]-- 每个质因子的数量
// numFactors -- n的质因子个数
// 返回n的约数个数
long countDivisor(unsigned long n, unsigned long factors[], long factorCount[], long &numFactors)
{
    int        nCount = 1;
    int        temp = 0;

    if(n <= 1)
    {
        numFactors = 1;
        return nCount;
    }

    numFactors = 0;

    // 质因子2的个数
    while((n&1) == 0)
    {
        n >>= 1;
        ++temp;
    }

    if(temp > 0)
    {
        nCount *= temp + 1;
        factors[numFactors] = 2;    // 存放具体的素因子
        factorCount[numFactors++] = temp;    // 存放每个素因子的个数
    }

    // 对于有最大约数个数的数n(n<2^32),n的最大质因子必定不会超过29
    long    pPrimes[10] = {3, 5, 7, 11, 13, 17, 19, 23, 29};

    for(int i=0; i<9; ++i)
    {
        if(n < pPrimes[i])
        {
            break;
        }
        temp = 0;    // 统计n含有素数pPrimes[i]的个数
        while(n%pPrimes[i] == 0)
        {
            ++temp;
            n /= pPrimes[i];
        }

        if(temp > 0)
        {
            nCount *= temp + 1;
            factors[numFactors] = pPrimes[i];
            factorCount[numFactors++] = temp;
        }
    }

    if(n > 1)
    {// 最后剩下一个质因子
        nCount *= 2;
        factors[numFactors] = n;
        factorCount[numFactors++] = 1;
    }

    return nCount;
}

// 根据某个数的质因子分解结果产生所有的约数
// *result -- 存放约数
// factors[]--质因子
// factorCount[]-- 每个质因子的数量
// numFactors -- 质因子个数
void GenerateDivisors(unsigned long *result, unsigned long factors[], 
            long factorCount[], long numFactors)
{
    long            i, j, k;
    long            last_count, cur_count;
    unsigned long    temp;

    result[0] = 1;
    last_count = cur_count = 1;

    for(i=0; i<numFactors; ++i)
    {
        temp = 1;
        for(j=0; j<factorCount[i]; ++j)
        {
            temp *= factors[i];
            for(k=0; k<last_count; ++k)
            {
                result[cur_count++] = result[k] * temp;
            }
        }
        last_count = cur_count;
    }
}

15 楼

// n: 范围
// result:结果,存放符合条件的那个数
// count:存入存放符合条件的那个数的约数的个数
// arr:存放那个数的所有约数,按照从小到大的顺序
void Search(unsigned long n, unsigned long *result,int *count,unsigned long arr[])
{
    unsigned long        i;

    assert(n >= 1);

    if(n == 1)
    {
        *result = 1;
        *count  = 1;
        arr[0]  = 1;
        return ;
    }

    long            factorCount[20], max_factorCount[20];    // 每个质因子的个数
    long            numFactors = 0, max_numFactors;        // n的质因子个数
    long            step;                // 循环步长(目的:提高速度)
    long            temp;
    unsigned long    start;

    if(n < 12)
        step = 2;
    else if(n < 120)
        step = 12;
    else if(n < 1680)
        step = 60;
    else if(n < 2520)
        step = 120;
    else if(n < 55440)
        step = 2520;
    else if(n < 720720)
        step = 27720;
    else if(n < 61261200)
        step = 360360;
    else
        step = 6126120;

    if(n > 4294410120)
        start = 4294410120;
    else
        start = n/step*step;

    *count  = 0;
    *result = 0;
    for(i=start; i>=start/2; i-=step)
    {
        temp = countDivisor(i, factorCount, numFactors);
        if(temp >= *count)
        {
            *count = temp;
            *result = i;
            // 复制该数的质因子的个数
            max_numFactors = numFactors;
            for(int j=0; j<numFactors; ++j)
            {
                max_factorCount[j] = factorCount[j];
            }

        }
    }

    // 产生该数的所有约数
    unsigned long    factors[12] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
    GenerateDivisors(arr, factors, max_factorCount, max_numFactors);

    qsort((void*)arr, *count, sizeof(unsigned long), cmp);
}

16 楼


先看下,还没想好!

17 楼

#include <iostream>

using namespace std;

inline void Search(unsigned long n){
       
       unsigned long result;
       int max=0;
       
       for(unsigned long m=n/2;m<=n;m++){
                    
                int count=0;
               
               
               for(int i=2;i<=m/2;i++){
                       if(m%i==0){
                                  count++;
                                  }
                                  }
                                  
                       if(count>max){
                       max=count ;                            
                       result=m;
                       }
                       
               count=0;
               
               }
       cout<<result<<"("<<max+2<<")"<<" <"<<1<<",";
       
        for(int i=2;i<=result/2;i++){
                       if(result%i==0){
                                  cout<<i<<",";
                                  }
                                  }
        
        cout<<result<<"> "<<endl;
               

       
       }
       

main(){
      unsigned long n;
      cin>>n;
      Search(n);
      system("pause");
       }

18 楼

/***********************************************
在N以内(小于等于N)找出一个数,要求:
   1.这个数的约数的个数达到最大,
   2.如果有好几个数满足条件1,仅取最小的那个数
***********************************************/
#include <stdio.h>

void Search(unsigned long n, unsigned long *result,int *count,unsigned long arr[]);
int GetDivisor(unsigned long n);    //得到n的约数个数
void main()
{
    unsigned long n;
    unsigned long *result;
    int *count;
    unsigned long arr[65535];
    result = new unsigned long;
    count = new int;
    printf("输入范围(1~N):N=");
    scanf("%ld",&n);
    Search(n,result,count,arr);
    printf("%ld\n%d\n%d",*result,*count,1);
    for(int i=1;i<*count;i++)
    {
        printf(",%d",arr[i]);
    }
    printf("\n");
}

void Search(unsigned long n, unsigned long *result,int *count,unsigned long arr[])
{
    unsigned long i;
    unsigned long num=1;    //数
    int divisor=1;    //约数个数
    int tmpdiv;
    int k;
    for(i=1;i<=n;i++)
    {
        tmpdiv=GetDivisor(i);
        if(divisor<tmpdiv)
        {
            divisor=tmpdiv;
            num=i;
        }
    }
    *result=num;
    *count=divisor;
    k=0;
    arr[k]=1;
    for(i=2;i<=num;i++)
    {
        if(num%i==0)
            arr[++k]=i;
    }
}

int GetDivisor(unsigned long n)
{
    unsigned long num=n;
    unsigned long div[31]={0};    //质因数数组
    int ddiv[31]={0};
    int sum=1;
    int i,k;
    unsigned long j;
    //以下获得质因数数组,比如n=360,则div={2,2,2,3,3,5}
    for(i=0;;i++)
    {
        for(j=2;j<=num;j++)
        {
            if(num%j==0)
            {
                num=num/j;
                break;
            }
        }
        div[i]=j;
        if(num==1)    break;
    }
    //以下获得另外一个数组,保存各质因数的个数,比如
    //div={2,2,2,3,3,5},则ddiv={3,2,1}
    i=0;
    ddiv[0]=1;
    for(k=1;k<=31;k++)
    {
        if(div[k]==div[k-1])
            ddiv[i]++;
        else if(div[k]!=0)
            ddiv[++i]=1;
        else break;
    }
    //得到因数的个数,如ddiv={3,2,1},则sum=(3+1)(2+1)(1+1)
    //因为如果num=(p1^n1)(p2^n2)(p3^n3)...
    //其中p1,p2,p3...是质数,n1,n2,n3...是自然数
    //则因数个数为(n1+1)(n2+1)(n3+1)...
    for(i=0;i<=31;i++)
    {
        if(ddiv[i]==0)    break;
        sum*=(ddiv[i]+1);
    }
    return sum;
}

19 楼


好东西,不顶是罪恶

20 楼

第一次正式参加.
这个题目还算容易,我以前写过类似的程序,求完全数的.
现在重新想来,原来以前的程序里面还有几个错误,现在完善了它,虽然速度下降了不少,但终于正确了.

//file: MostSubmultiple.c
//compiler: VC++ 6.0
#include "stdio.h"
#include "math.h"
#include "stdlib.h"
/*
基本思路: 搜索1 到 Num的平方根 范围内的约数,如果m是其约数,则 Num/m也是其约数
另外,由于求Num的平方根SquareBoot时sqrt函数是向下取整的,所以如果SquareBoot恰好
是Num的平方根,则只能算一个.如果不是而只是Num的一个约数,那么Num / SquareBoot也是
就多了两个约数
*/

//实际上,如果n大于2,则在n范围内约数个数最多的数中最小的一定是偶数,
//代码中屏蔽了处理奇数的部分,楼主如果觉得有奇特情况需要就把注释前后去掉

//数据类型采用unsigned long int
typedef unsigned long int ULI;

void Search(ULI n, ULI *pResult,ULI *pCount,ULI arr[]);

ULI Submultiple(ULI Num);//求出一个数的约数个数,这个函数实际上可以直接写在Search里面,这样效率更好

void Store(ULI Num, ULI TotalSubmultiple,ULI arr[]);//将符合条件的那个数的约数列举出来,存储在arr[]

#define MaxMemory 1000000
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void main()
{
    ULI i, Count, Result;
    ULI *pArr = NULL;
    ULI n = 100000;

    /*
    for (i=2; i<=15; i++){
        Count = Submultiple(i);
        printf("%u 有 %u个约数 \n", i, Count);
    }
    */

    pArr = (ULI*)malloc(MaxMemory);//用来存放那个数的所有约数,按照从小到大的顺序
    Search(n, &Result, &Count, pArr);

    printf("%u以内约数最多且最小的数是 %u ,共有 %u 个约数,分别是: \n", n, Result, Count);

    for (i = 0; i < Count; i++){
        printf("%u ",*(pArr +i));
    }    

    free(pArr);
    pArr = NULL;
}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void Search(ULI n, ULI *pResult,ULI *pCount,ULI arr[])
{
    ULI i;
    ULI MostSubmultiple = 1, Count;
    ULI Result;

    //for (i = 2; i <= n; i++){
    //    
    //}
    //实际上,如果n大于2,则在n范围内约数个数最多的数中最小的一定是偶数.
    for (i = 2; i <= n; i += 2){
        Count = Submultiple(i);
        if (Count > MostSubmultiple){
            MostSubmultiple = Count;
            Result = i;
        }
    }

    Store(Result, MostSubmultiple, arr);
    *pResult = Result;
    *pCount = MostSubmultiple;
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//求出一个数的约数个数
ULI Submultiple(ULI Num)
{
    ULI Count;//约数的个数,最后要将1加入去
    ULI i;
    ULI SquareBoot = (ULI)sqrt(Num);//搜索到Num的平方根为止

    Count = 0;
    //if ( (Num & 1) == 0 ){//it's even 偶数
        //搜索1 到 Num的平方根 范围内的约数,如果m是其约数,则 Num/m也是其约数
        //所以将最后结果乘2,最后还要对Num % Mid进入讨论
        for (i = 1; i < SquareBoot; i++){
            //一个数除Num的余数,如果为0,则说明它是Num的约数
            if (Num % i == 0) {Count++;}
        }
    //}
    /*
    else {//it's odd 奇数,只搜索小于Mid的奇数
        for (i = 1; i < SquareBoot; i += 2){
            if (Num % i == 0) {Count++;}
        }
    }
    */

    Count <<= 1;//乘2

    //如果Mid恰好真是Num的平方根(前面用sqrt函数是向下取整的)
    if (SquareBoot * SquareBoot == Num){
        Count++;    //这里不能像前面一样乘2了,不然就多出了一个
    }
    else if (Num % SquareBoot == 0){//如果只是它的一个约数
        Count += 2;    //Num / Mid也是它的一个约数
    }
    
    return Count;
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//将符合条件的那个数的约数列举出来,存储在arr[]
//参数1: 符合条件的那个数 2.其约数的个数(以便定位) 3.要存储的数组地址
void Store(ULI Num, ULI TotalSubmultiple,ULI arr[])
{
    ULI i, Count;    
    ULI SquareBoot = (ULI)sqrt(Num);//搜索到Num的平方根为止

    Count = 0;
    //从 arr[0]存储到 arr[--TotalSubmultiple]
    for (i = 1; i < SquareBoot; i++){
        //如果m是其约数,则 Num/m也是其约数
        if (Num % i == 0) {
            arr[Count++] = i;
            arr[--TotalSubmultiple] = Num / i;
            //由于这里实际上重复执行了两次 Num 除以 i,如果用汇编实现,效率更好!
        }
    }

    //如果Mid恰好真是Num的平方根(前面用sqrt函数是向下取整的)
    if (SquareBoot * SquareBoot == Num){
        arr[Count++] = SquareBoot;
    }
    else if (Num % SquareBoot == 0){//如果只是它的一个约数
        arr[Count++] = SquareBoot;    //Num / Mid也是它的一个约数
        arr[--TotalSubmultiple] = Num / SquareBoot;
    }
    
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

我来回复

您尚未登录,请登录后再回复。点此登录或注册