【编程之美】2.8 找符合条件的整数

给定一个正整数N,求一个最小的正整数M(M > 1),使得N * M的十进制表示中只有0和1。

我的思路:

从最低位到最高位找M,每次使得乘积的最后面多一位符合0、1的条件。

那么先找能够让末尾数字变成0的备选项 举例若N的个位数是9  考虑从后面来的进位 c 让 x * 9 + c 的末尾是0或1

设个位数字为9 则eligibleNum中存储的数字
eligibleNum[0][0] = 0 因为9 * 0 + 0 = 0 末尾符合0或1
eligibleNum[0][0] = 9 因为9 * 9 + 0 = 81 末尾符合0或1
eligibleNum[1][0] = 0 因为9 * 1 + 1 = 10 末尾符合0或1
eligibleNum[1][1] = 1 因为9 * 0 + 1 = 1 末尾符合0或1

...

这样,对每一位判断时 只要知道了进位c N的个位数字 就可以直接查找得到符合条件的备选项

然后利用广度优先搜索,查找最小的M

代码中的问题:这个代码无法判断无解的情况 而且 在答案的位数过多的时候会报错 判断进位由于跟已有的低位都有牵扯 导致必须使用这个数字的乘法 导致大数会溢出

总之,是个烂代码。唯一欣慰的是练习了一下广度优先搜索

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

//判断n里面是否只有0和1
bool IsAll01(int n)
{
    while(n)
    {
        if((n % 10 == 0) || (n % 10 == 1))
        {
            n = n / 10;
        }
        else
        {
            return false;
        }
    }
    return true;
}
/*
设个位数字为9  则eligibleNum中存储的数字
eligibleNum[0][0] = 0  因为9 * 0 + 0 = 0 末尾符合0或1
eligibleNum[0][0] = 9  因为9 * 9 + 0 = 81 末尾符合0或1
eligibleNum[1][0] = 0  因为9 * 1 + 1 = 10 末尾符合0或1
eligibleNum[1][1] = 1  因为9 * 0 + 1 = 1 末尾符合0或1
...
*/
void getEligibleNum(int single, vector<vector<int>> * eligibleNum)
{
    for(int i = 0; i < 10; i++) //对每一个被加数
    {
        vector<int> tmp;

        for(int j = 0; j < 2; j++) //对每一个允许的和的末尾数0、1
        {
            for(int k = 0; k < 10; k++)
            {
                int multi = k * 10 + j - i;
                if((multi >= 0) && (multi % single == 0) && (multi/single < 10))
                {
                    tmp.push_back(multi/single); //把符合条件的数存入
                }
                else
                {
                    continue;
                }

            }
        }

        eligibleNum->push_back(tmp);
    }
}

////获得所有符合条件的答案 广度优先搜索
void getAllAllowedAns(int N, vector<vector<int>> * eligibleNum,vector<vector<int>> * allAllowedAns, queue<vector<int>> Q)
{
    bool isHaveAns = false; //当前循环下是否已经找到答案
    int currentLength = Q.front().size();
    while(Q.front().size() == currentLength)
    {
        int addNum;
        int alreadyNum = 0;
        int pow = 1;
        int currentMulti = 0;
        if(currentLength == 0)
        {
            addNum = 0;
        }
        else
        {
            for(int i = currentLength - 1; i >= 0; i--)
            {
                alreadyNum = alreadyNum * 10 + Q.front()[i];
                pow *= 10;
            }
            currentMulti = alreadyNum * N;

            addNum = (currentMulti / pow ) % 10;
        }

        vector<int>::iterator it;
        for(it = (*eligibleNum)[addNum].begin(); it < (*eligibleNum)[addNum].end(); it++) //遍历所有备选项
        {
            vector<int> currentAns = Q.front();
            currentAns.push_back(*it);

            if(IsAll01((*it) * N + currentMulti / pow)) //符合条件的答案
            {
                allAllowedAns->push_back(currentAns);
                isHaveAns = true;
            }
            else
            {
                Q.push(currentAns);
            }
            currentAns.pop_back();
        }

        Q.pop();
    }

    if(isHaveAns == true && currentLength != 0)
    {
        return;
    }
    else
    {
        getAllAllowedAns(N, eligibleNum, allAllowedAns, Q);
    }
}

char * getSmallestM(int N)
{
    vector<vector<int>> eligibleNum; //符合条件的数字
    vector<vector<int>> allAllowedAns;
    vector<int> tmpAns;
    queue<vector<int>> Q;
    Q.push(tmpAns);
    int single; //数字N的个位上的数
    while(N % 10 == 0) //N末尾的0对结果没有影响去掉
        N = N / 10;
    single = N % 10;
    getEligibleNum(single, &eligibleNum);
    getAllAllowedAns(N, &eligibleNum, &allAllowedAns, Q);

    return NULL;
}

int main()
{
    getSmallestM(35);

    return 0;
}

下面上高大上的答案:

答案没有考虑M,而是从找M*N入手。因为M*N只有0和1 表示更为简便。

用一个向量存储X % N = i 的每一种 i 情况下最小的X

对与10^k + Y 的情况

先计算 j = 10^k % N

然后用 (j + 已有的余数)% N 若出现了新的余数则存起来

且 100 % N = (10 % N) * 10 % N 利用这种规律可以避免10^k的大数表示

这种情况下 只要维护一个N个空间的余数向量 每个元素是个不定长的数组,存储M*N的第几位是1 也避免了大数的表达

且遍历时间很少 如果最后答案有K位 每一位只需遍历N次 最终只需遍历(K-1)*N步 非常快

代码:BigInt[0] 就是最小的M * N的值

#include <iostream>
#include <vector>
using namespace std;

typedef unsigned char uchar;

//BigInt中存储M * N的结果中 1的位置
void GetSmallestM(int N, vector<vector<uchar>> * BigInt)
{
    int i , j;
    vector<uchar> init;
    init.clear();
    //初始化 每一个都存个空的
    for(i = 0; i < N; i++)
    {
        BigInt->push_back(init);
    }
    (*BigInt)[1].push_back(0); //余数为1的最小的数肯定是1 第0位是1

    int NoUpdate = 0;
    //i 表示当前最高位是 10^i 次方   j表示 10^i % N的值 100 % N = ((10 % N) * 10) % N 注意 j避免表示大数的方法
    for(i = 1, j = 10 % N; ; i++, j = (j * 10) % N)
    {
        bool flag = false;
        //出现新的余数 存储
        if((*BigInt)[j].size() == 0)
        {
            flag = true;
            (*BigInt)[j].clear();
            (*BigInt)[j].push_back(i);
        }
        //对当前已有的余数遍历 判断 (j + k) % N是否出现新余数
        for(int k = 1; k < N; k++)
        {
            if((*BigInt)[j].size() > 0 &&
                ((*BigInt)[k].size() > 0 && i > (*BigInt)[k][(*BigInt)[k].size() - 1]) //这个条件表示当前的余数非空 且 当前的余数不是因为当前i下 早些的k循环处理中产生的 BigInt的每一项中不能有相同的数字
                && (*BigInt)[(k + j) % N].size() == 0)
            {
                flag = true;
                (*BigInt)[(k + j) % N] = (*BigInt)[k];
                (*BigInt)[(k + j) % N].push_back(i);
            }
        }
        if(flag == false)
            NoUpdate++;
        else
            NoUpdate = 0;

        //如果经过一个循环节没有更新 则无解 跳出 因为循环N次都没有出现新的余数 而N的余数一共最多就N种
        //找到答案了也跳出
        if(NoUpdate == N || (*BigInt)[0].size() > 0)
            break;
    }
    if((*BigInt)[0].size() == 0)
    {
        cout << "M not exist" << endl;
    }
    else
    {
        return;
    }
}

int main()
{
    vector<vector<uchar>>  BigInt;
    GetSmallestM(99, &BigInt);
    return 0;
}
时间: 2024-09-29 01:36:30

【编程之美】2.8 找符合条件的整数的相关文章

编程之美-2.8 找到符合条件的整数

一.问题描述 任意给定一个正整数N,求一个最小正整数M(M>1),使得N*M的十进制形式只含1和0. 比如 N=99,M=1 122 334 455 667 789 ,N*M=111 111 111 111 111 111; M就是当N=99时,符合条件的数 二.解题思路 考虑将问题转化为:找只含有0和1的能被N整除的最小正整数.可以看出这是和原问题等价的. 那么需要将0和1组成的所有数从小到大遍历吗? 这样的话,如果寻找的数是k位,则需要搜索2k-1个数才能得到结果. 这里采用的方式是在计算中

编程之美2.8 | 找符合条件的整数

思路还是相当地巧妙. 求余数的话,(a+b)%n=(a%n+b%n)%n; 用vector来表示整数的话(出现1的位置),可以避免溢出. 注意第20行,在更新remainders[(j+r)%n]时,要确保每个remainders的每个序列都是递增的,不能存在相等的情况. 1 #include <time.h> 2 #include <math.h> 3 #include <stdlib.h> 4 5 using namespace std; 6 7 long long

编程之美2.8——找符合条件的整数

任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0. 如N=3,M=39,N*M=111. [思路] 这么难的思路打死我也想不到[email protected][email protected]|||||.. 将题目转换为,求一个数X,使得X%N=0且X的十进制表示只含有1和0. 维护一个“余数数组”,对于从0到N-1的每一个余数,都有相应的最小X: 高位可以利用低位的余数归队,X=10^k+Y(10的k次方,^表示次方)X%N=(10^k%N+Y%

第2章 数字之魅——找符合条件的整数

找符合条件的整数 问题描述 任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0. 解决这个问题首先考虑对于任意的N,是否这样的M一定存在.可以证明,M是一定存在的,而且不唯一.简单证明:因为 这是一个无穷数列,但是数列中的每一项取值范围都在[0, N-1]之间.所以这个无穷数列中间必定存在循环节.即假设有s,t均是正整数,且s<t,有 .于是循环节长度为t-s.于是10^s = 10^t.因此有:,所以 例如,取N=3,因为10的任何非负次方模3都为

【编程之美】找符合条件的整数

任意给定一个正整数N,求一个最小的正整数M(M > 1),使得N*M的十进制表示形式里只含有1和0. 看了题目要求之后,我们首先想到从小到大枚举M的取值,然后再计算N*M,最后判断它们的乘积是否只含有1和0.大体思路可以用下面的伪代码实现: 1 for (M = 2; ; M++) 2 { 3 product = N * M; 4 if (hasOnlyOneAndZero(product)) 5 output N, M, product, and return; 6 } 但问题很快就出现了,什

编程之美---找符合条件的整数

题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0. 解法:原问题转化为求一个最小的正整数X,使得X的十进制表示形式里只含有1和0,并且X被N整除.于是乎就成了遍历二进制整数一样遍历X的各个取值,但是如果X的最终结果又K位,则要循环搜索2K 次.因此,可以建立一个长度为N的“余数信息数组”,这个数组的第i位保留已经出现的最小的模N为i的X.用BigInt[i]可能很大,只须记下1的位置即可. 1 for(i=0;i<N;i++) 2 BigIn

(转)找符合条件的整数

题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0.解决这个问题首先考虑对于任意的N,是否这样的M一定存在.可以证明,M是一定存在的,而且不唯一.简单证明:因为 这是一个无穷数列,但是数列中的每一项取值范围都在[0, N-1]之间.所以这个无穷数列中间必定存在循环节.即假设有s,t均是正整数,且s<t,有 .于是循环节长度为t-s.于是10^s = 10^t.因此有:,所以 例如,取N=3,因为10的任何非负次方模3都为1,所以循环节周期为1.

找符合条件的整数

题目: 任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式只含有1和0 . 分析: 将问题"求一个最小的正整数M,使得N*M的十进制表示形式里只含有1和0"转换为求一个最小的正整数X,使得X的十进制表示形式里只含有1和0,并且X被N整除.

编程之美 找出符合条件的整数

好不容易把内容看懂~ 最主要的一句话:只需要将10k%N的结果与余数信息数组里非空的元素相加,再去模N,看看会不会出现新的余数~ 时间太紧迫~先把自己写的代码贴上,以后再详解 1 int FindMin(int N) 2 { 3 if(N <= 1) 4 return N; 5 6 int* A = new int[N];//这个是记录模N余i之后的数值 7 8 memset(A, -1, sizeof(int) * N); 9 int factor = 1; 10 A[1] = 1; 11 1