编程之美 - 寻找合适的数字

问题描述:

给一个整数N,求一个整数M,使M*N 的十进制结果中只有 1 和 0。

问题分析:

问题从结果入手,十进制表示只有 1 和 0,这个数字的集合有规律,例如: 1,10,11,100,101,110,111.....
10k次方与前面的每个的数的和。
例如 k=1  ==> 10 ,在 10这个阶段的数字有 1 + 10 = 11
例如 k=2  ==> 100 ,在 100这个阶段的数字有 1 + 100 = 101, 10+100=110,11+100=111

方式1: 从1开始,尝试每一个1,0组合的结果Result,找到 Result % N == 0的Result,再用 Result / N就得到M。
可以做到,但效率不高。

方式2:每个数字的遍历会比较浪费时间,书中用余数的方式进行考虑。
假设最后的乘积为X,X有可能是10的k次方,也有可能是10的k次方与有效数集合中之前一个数的和,
假设  X = 10^k + Y,Y也满足只有0和1组成,类似于 111 = 100 + 11。如果X是期望的结果,那么 一定有(10^k % N + Y % N) == N

可以定义一个大小为N的数组(X%N的余数的范围是从1~N-1),用来保存X的值的余数。
如果数组中两个数组下标(这里的数组下标就是余数)的和为N,并且两个元素都有效,那最后的结果就是这两个元素的和。

用数字3举例   N = 3,   余数数组  mod_list[3+1]  定义有 3+1个元素,方便计算

k = 0     X = 1       mod 3 = 1    mod_list[1]  =  1
k = 1     X = 10     mod 3 = 1   因为 mod_list[1]  =  1 已经存在了,所以不用改变。
             推导出  mod_list[1+1]  =  11   即 11%3=2   mod_list[2] = 11
k = 2     X = 100   mod 3 = 1,这时 mod_list[2] 已经存在了,而且 2+1=3
那最后的结果就是   100 + mod_list[2] = 100 + 11 = 111

这种方式是 3次取模,一次加法运算。

如果用遍历的方式则需要遍历(1, 10, 11, 100, 101, 110, 111) 7个数字,7次取模。2^k - 1次

#include <iostream>
#include <vector>

using namespace std;

int find2(int num)
{
    int result = 0, k = 0, step = 1;
    int i = 0, j = 0, curr = 0;
    bool exist = false;
    int* plist = new int[num+1];
    vector<int> list;
    memset(plist, 0, sizeof(int)*(num+1));

    while (result == 0)
    {
        k = step % num;
        if (k == 0)
        {
            result = step;
        }
        else if (plist[num-k] != 0)
        {
            result = plist[num-k] + step;
        } 
        else
        {
            list.clear();
            if (plist[k] == 0)
            {
                list.push_back(k);
                plist[k] = step;
            }
            exist = false;
            for (i = 1; i < num+1; i++)
            {
                curr = (i+k) % num;
                for (j = 0; j < list.size(); j++)
                {
                    if (list[j] == i)
                        exist = true;   // make sure the same value not calculate twice
                }

                if ((plist[i] != 0) && (!exist) && (plist[curr] == 0))
                {
                    list.push_back(curr);
                    plist[curr] = step + plist[i];
                }
            }
        }
        step *= 10;
    }

    cout << num << " * " << result/num << " = " << result << endl << endl;

    delete[] plist;
    plist = NULL;
    return result;
}

int find(int num)
{
    int result = 0;
    int step = 10, i = 0, curr = 0, cnt = 0;
    vector<int> list;

    list.push_back(1);

    while(list[curr]%num != 0)
    {
        if (curr == list.size()-1)
        {
            list.push_back(step);
            cnt = list.size() - 1;
            for (i = 0; i < cnt; i++)
                list.push_back(list[i]+step);
            step *= 10;
        }
        curr++;
    }
    result = list[curr];
    cout << num << " * " << list[curr]/num << " = " << list[curr] << endl << endl;
    return result;
}

void main()
{
    int test[] = {5, 12, 11, 6, 9, 21, 30};
    int len = sizeof(test)/sizeof(test[0]);
    int i = 0; 

    for (i = 0; i < len; i++)
        find(test[i]);
    cout << "\n\n=======================================\n" <<endl;
    for (i = 0; i < len; i++)
        find2(test[i]);
    cin >> i;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值