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

题目:

任意给定一个正整数N,求一个最小的正整数MM>1),使得N * M的十进制表示形式里只含有10

看了题目,我很自然的想到了枚举。然后看N * M的十进制是否只包含10。当然,这种暴力的解法不是我们想要的。

换一种枚举方法,我们枚举N*M的取值效果怎么样呢?因为N * M的只包含10,所以对于K位的N*M,需要搜索2K次方。

因为找的是最小整数,所以可以采用BFS(宽度优先搜索)。如果最终解使得N * Mk位,那么总共要搜索2的(k+1)次方。有什么方法可以剪枝么?

答案是肯定的。

N = 3时,看如下搜索树,括号内容表示mod N的值:


X = 101 Y=110,可知XY同余。

所以10X + 010Y + 0同余,10X + 110Y + 1同余,并且易知,从X继续往下搜索的值,一定比Y的小。要求最小的M,我们还有必要拓展Y节点么?

综上所述,采用BFS方法,在每一层中,如果出现余数相同的节点,只需要拓展最先出现的即可。


《编程之美》介绍的是另一种更妙的方法。

枚举M也好,BFS搜索N * M也好,我们有没有想过N * M超出整数范围?难道对于大整数,我们还要手写一个大数类?好麻烦…………


首先要解决的是,对于大整数,我们如果表示呢?

因为结果N * M只包含10,我们可以用一个vector保留1出现的位数。比如11001,可以用 {0 3 4} 表示。这种方法很省空间。

回想一下刚才提到的,BFS的剪枝方法——同余的数只保留最小的!

看个实例大家就好懂了:

如果知道100 mod 3 =1 

 10 mod 3 = 1

  1 mod 3 = 1

110 mod 3 = 2

如何找到比110更小而且mod 3 = 2的数呢?用1去替换110中的10OK了。因为110同余,所以保留余数最小的即可,毕竟我们找的是满足mod N = 0最小且只含有01的数。


我的代码如下:

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

void display(vector<int> &v)
{	
	int t = 0;
	for (int i = v.size() - 1;i >= 0;i--)
	{
		t = v[i];
		cout << '1';
		while(i > 0 && --t != v[i - 1])
			cout << '0'; 
	}
	while(t--)
		cout << '0';
	cout << endl;
}
void searchTheNum(vector<vector<int>> &v, int n)
{
	for (int i = 0;i < n;i++)
	{
		vector<int> nv;
		nv.clear();
		v.push_back(nv);
	}
	v[1].push_back(0);
	int j;
	for (int i = 1, j = 10 % n; ;i++, j = (j * 10) % n)
	{
		if (0 == v[j].size())
		{
			v[j].push_back(i);
		}
		for (int k = 0;k < v.size();k++)
		{
			if (v[k].size() > 0
				&& v[(k + j) % n].size() == 0
				&& i > v[k][v[k].size() - 1])
			{								
				v[(k + j) % n] = v[k];
				v[(k + j) % n].push_back(i);
			}
		}

		if (v[0].size())
		{
			display(v[0]);
			return;
		}
	}
}
int main()
{
	vector<vector<int>> v;
	int n = 99;
	searchTheNum(v, n);
	return 0;
}

对于书中的拓展问题1

对于任意的N,一定存在M,使得N*M的乘积的十进制表示只有01吗?

我百度到的答案是肯定存在,所以我的代码就假设一定存在解。

实际上书中的代码用了抽屉原理判断是否有解。


拓展问题2

怎么找出满足题目要求的和 M,使得N * M < 216次方,且N + M最大?

根据N定出M的上界,并在这个范围内进行查找。

用如上的思路都可以解决。

BFS:先拓展值较大的点,每层同余的数只保留值大的。

书中的方法:也是要保留大值。

这些都还要加上边界的判断。


参考资料:

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

http://www.cnblogs.com/bvbook/archive/2009/02/06/1385448.html 


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值