题目链接:《Humble Numbers》

  题意:如果一个数的所有质数因子都来自于 { 2, 3, 5, 7 } 这个集合,就把这个数字叫做“谦逊数”,或者“低调数”(Humble Number),现在给出一个数字 i (1 <= i <= 5842),要求输出第 i 个 humble number。比如说,前 20 个 humble number 是:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27。

 

  分析:这个题目的描述是非常简单的。从 i 的限定范围最大是 5842 以及范例输出来看,很显然出题人暗示了我们这个题目中涉及到的 humber number 不会超出 int 的范围。因此我们可以放心的使用 int,而不用担心超出表示范围。

  其次可以很容易的想到,需要一个 int 数组,把需要的所有 humber number 放进去作为供查询的表。但是生成这个表会比较耗时,所以很容易超过 2 秒的运行时间限制。所以我们需要更快的建立这个数组,则观察这个序列,因为所有的数字都是如下形式:

  x[i] = (2 ^ k[0]) * (3 ^ k[1]) * (5 ^ k[2]) * (7 ^ k[3]);

 

    ZOJ 1095. Humble Numbers

 

  这里 k 是一个数组 ( int [ 4 ] ),元素表示 2, 3, 5, 7 这四个因子的幂,因此考虑从 x [ i ] 跳到下一个 x [ i + 1] 时,就是数字的这 4 个因子的幂在发生变化。因此只要知道从 x [ i ] 变化到 x [ i + 1 ] 时,数组 k 如何变化即可。因此我们需要找出前 5842 个 humber number 中的所有规则,这样就可以快速的得到前 5842 个 humber number,组建成我们要查的表。

  观察前面几个数字,很容易发现出这些规则,例如:

  1->2, 2->3, 3->4, 4->5, 5->6, ...

  从 10 到 12 本质上还是应用的 5->6 。因此只有相邻且互质的数字(a, b),才属于我们要找的规则(a -> b),其他的相邻数字都是应用了上述规则中的某一条。

  同时这些规则还有优先级的顺序之分,从表面上看,应该是数字 a 和 b 越大,规则越优先。但实际上并非如此,例如:

  从 75 到 80 ,应用的实际规则是 15->16 ,而不是 25->27 (这会产生从 75 变成 81,跳过了 80)。因此规则的优先级排序需要按照 a->b 的放大倍数进行从小到大排序。放大倍数(b / a)越小的规则,越优先。考虑到这个规则很多(实际有 76 条),而且涉及的数字很大,所以人工找出所有规则是不现实的。所以我使用一个程序(称之为代码生成器)来专门找出所有的规则,并将其输出成一个函数的代码,函数的名称是 GetNext,含义是根据当前的 humble number ,找出下一个 humble number。如下:

#include <vector>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <string.h>

typedef struct tagNODE
{
    int from;
    int to;
    double ratio; //= to / from;
} NODE;

//x1, x2 是否是已经排好序的。
bool IsSuccessive(NODE x1, NODE x2)
{
    return x1.ratio < x2.ratio;
}

void Init(std::vector<int> &v1, int nSize)
{
    v1.reserve(nSize);
    v1.clear();
    v1.push_back(1);
    int nNumber = 2;
    int tmp;
    while(v1.size() < nSize)
    {
        tmp = nNumber;
        while((tmp % 2) == 0) tmp /= 2;
        while((tmp % 3) == 0) tmp /= 3;
        while((tmp % 5) == 0) tmp /= 5;
        while((tmp % 7) == 0) tmp /= 7;
        if(tmp == 1)
            v1.push_back(nNumber);
        ++nNumber;
    }
}

//x1, x2 是否是互质的(没有公共因子)
bool no_comm_factor(int x1, int x2)
{
    if(x1 % 2 == 0 && x2 % 2 == 0)
        return false;
    if(x1 % 3 == 0 && x2 % 3 == 0)
        return false;
    if(x1 % 5 == 0 && x2 % 5 == 0)
        return false;
    if(x1 % 7 == 0 && x2 % 7 == 0)
        return false;
    return true;
}

//give x, find k;
//x = 2^k[0] * 3^k[1] * 5^k[2] * 7^k[3];
void GetK(int x, int k[4])
{
    memset(k, 0, sizeof(int) * 4);
    while((x & 1) == 0) 
    {
        x /= 2;
        k[0]++;          
    }
    while(x % 3 == 0)
    {
        x /= 3;
        k[1]++;
    }
    while(x % 5 == 0)
    {
        x /= 5;
        k[2]++;
    }
    while(x % 7 == 0)
    {
        x /= 7;
        k[3]++;
    }
}

int main(int argc, char* argv[])
{
    std::vector<int> v1;
    //计算出前 5842 个 humber number,这一步需要花比较长的时间。
    Init(v1, 5842);

    //找出所有策略(即找出所有的相邻的互质的 humber number 对)。
    std::vector<NODE> nodes;
    for(int i = 5841; i > 0; --i)
    {
        if(no_comm_factor(v1[i - 1], v1[i]))
        {
            NODE item;
            item.from = v1[i - 1];
            item.to = v1[i];
            item.ratio = item.to * 1.0 / item.from;
            nodes.push_back(item);
        }
    }

    //按照放大倍数从小到大进行规则排序。
    std::sort(nodes.begin(), nodes.end(), IsSuccessive);

    int iRule = 0;
    int k1[4], k2[4];
    char buf[1024], *pos;
    FILE *fp = fopen("GetNextK_code.cpp", "w");
    fputs("void GetNext(int* k)\n{\n", fp);
    typename std::vector<NODE>::const_iterator it;
    for(it = nodes.begin(); it != nodes.end(); ++it)
    {
        ++iRule;

        GetK(it->from, k1);
        GetK(it->to,   k2);

        if(iRule == 1) strcpy(buf, "\tif(");
        else if(iRule == nodes.size()) strcpy(buf, "else");
        else strcpy(buf, "\telse if(");
        pos = buf + strlen(buf);

        if(k1[0]) 
        {
            sprintf(pos, "k[0] >= %d && ", k1[0]);
            pos += strlen(pos);
        }
        if(k1[1])
        {    
            sprintf(pos, "k[1] >= %d && ", k1[1]);
            pos += strlen(pos);
        }
        if(k1[2])
        {
            sprintf(pos, "k[2] >= %d && ", k1[2]);
            pos += strlen(pos);
        }
        if(k1[3])
        {
            sprintf(pos, "k[3] >= %d && ", k1[3]);
            pos += strlen(pos);
        }

        if(iRule == nodes.size())
        {
            //最后一条规则
            sprintf(pos,  " //(rule %d) %d -> %d (%lf);\n\t{\n", 
                iRule, it->from, it->to, it->ratio);
        }
        else
        {
            pos -= 4; //remove " && ";
            sprintf(pos, ") //(rule %d) %d -> %d (%lf);\n\t{\n", 
                iRule, it->from, it->to, it->ratio);
        }
        pos += strlen(pos);

        //From
        if(k1[0])
        {
            sprintf(pos, "\t\tk[0] -= %d;\n", k1[0]);
            pos += strlen(pos);
        }
        if(k1[1])
        {
            sprintf(pos, "\t\tk[1] -= %d;\n", k1[1]);
            pos += strlen(pos);
        }
        if(k1[2])
        {
            sprintf(pos, "\t\tk[2] -= %d;\n", k1[2]);
            pos += strlen(pos);
        }
        if(k1[3])
        {
            sprintf(pos, "\t\tk[3] -= %d;\n", k1[3]);
            pos += strlen(pos);
        }

        //To
        if(k2[0])
        {
            sprintf(pos, "\t\tk[0] += %d;\n", k2[0]);
            pos += strlen(pos);
        }
        if(k2[1])
        {
            sprintf(pos, "\t\tk[1] += %d;\n", k2[1]);
            pos += strlen(pos);
        }
        if(k2[2])
        {
            sprintf(pos, "\t\tk[2] += %d;\n", k2[2]);
            pos += strlen(pos);
        }
        if(k2[3])
        {
            sprintf(pos, "\t\tk[3] += %d;\n", k2[3]);
            pos += strlen(pos);
        }
        strcpy(pos, "\t}\n");
        fputs(buf, fp);
        fflush(fp);
    }
    fputs("}\n", fp);
    fclose(fp);
    return 0;
}
Code_Generator

相关文章: