编程之美-找符合条件的整数
2012-03-29 19:57
316 查看
题目:
任意给定一个正整数N,求一个最小的正整数M(M>1),使得N * M的十进制表示形式里只含有1和0。
看了题目,我很自然的想到了枚举。然后看N * M的十进制是否只包含1和0。当然,这种暴力的解法不是我们想要的。
换一种枚举方法,我们枚举N*M的取值效果怎么样呢?因为N * M的只包含1和0,所以对于K位的N*M,需要搜索2的K次方。
因为找的是最小整数,所以可以采用BFS(宽度优先搜索)。如果最终解使得N * M有k位,那么总共要搜索2的(k+1)次方。有什么方法可以剪枝么?
答案是肯定的。
当N = 3时,看如下搜索树,括号内容表示mod N的值:
令X = 101 Y=110,可知X与Y同余。
所以10X + 0与10Y + 0同余,10X + 1与10Y + 1同余,并且易知,从X继续往下搜索的值,一定比Y的小。要求最小的M,我们还有必要拓展Y节点么?
综上所述,采用BFS方法,在每一层中,如果出现余数相同的节点,只需要拓展最先出现的即可。
《编程之美》介绍的是另一种更妙的方法。
枚举M也好,BFS搜索N * M也好,我们有没有想过N * M超出整数范围?难道对于大整数,我们还要手写一个大数类?好麻烦…………
首先要解决的是,对于大整数,我们如果表示呢?
因为结果N * M只包含1和0,我们可以用一个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中的10就OK了。因为1和10同余,所以保留余数最小的即可,毕竟我们找的是满足mod N = 0最小且只含有0和1的数。
我的代码如下:
对于书中的拓展问题1:
对于任意的N,一定存在M,使得N*M的乘积的十进制表示只有0和1吗?
我百度到的答案是肯定存在,所以我的代码就假设一定存在解。
实际上书中的代码用了抽屉原理判断是否有解。
拓展问题2:
怎么找出满足题目要求的N 和 M,使得N * M < 2的16次方,且N + M最大?
根据N定出M的上界,并在这个范围内进行查找。
用如上的思路都可以解决。
BFS:先拓展值较大的点,每层同余的数只保留值大的。
书中的方法:也是要保留大值。
这些都还要加上边界的判断。
参考资料:
《编程之美》 2.8 找符合条件的整数
/article/4952676.html
任意给定一个正整数N,求一个最小的正整数M(M>1),使得N * M的十进制表示形式里只含有1和0。
看了题目,我很自然的想到了枚举。然后看N * M的十进制是否只包含1和0。当然,这种暴力的解法不是我们想要的。
换一种枚举方法,我们枚举N*M的取值效果怎么样呢?因为N * M的只包含1和0,所以对于K位的N*M,需要搜索2的K次方。
因为找的是最小整数,所以可以采用BFS(宽度优先搜索)。如果最终解使得N * M有k位,那么总共要搜索2的(k+1)次方。有什么方法可以剪枝么?
答案是肯定的。
当N = 3时,看如下搜索树,括号内容表示mod N的值:
令X = 101 Y=110,可知X与Y同余。
所以10X + 0与10Y + 0同余,10X + 1与10Y + 1同余,并且易知,从X继续往下搜索的值,一定比Y的小。要求最小的M,我们还有必要拓展Y节点么?
综上所述,采用BFS方法,在每一层中,如果出现余数相同的节点,只需要拓展最先出现的即可。
《编程之美》介绍的是另一种更妙的方法。
枚举M也好,BFS搜索N * M也好,我们有没有想过N * M超出整数范围?难道对于大整数,我们还要手写一个大数类?好麻烦…………
首先要解决的是,对于大整数,我们如果表示呢?
因为结果N * M只包含1和0,我们可以用一个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中的10就OK了。因为1和10同余,所以保留余数最小的即可,毕竟我们找的是满足mod N = 0最小且只含有0和1的数。
我的代码如下:
#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的乘积的十进制表示只有0和1吗?
我百度到的答案是肯定存在,所以我的代码就假设一定存在解。
实际上书中的代码用了抽屉原理判断是否有解。
拓展问题2:
怎么找出满足题目要求的N 和 M,使得N * M < 2的16次方,且N + M最大?
根据N定出M的上界,并在这个范围内进行查找。
用如上的思路都可以解决。
BFS:先拓展值较大的点,每层同余的数只保留值大的。
书中的方法:也是要保留大值。
这些都还要加上边界的判断。
参考资料:
《编程之美》 2.8 找符合条件的整数
/article/4952676.html
相关文章推荐
- 编程之美-找符合条件的整数
- 编程之美 找出符合条件的整数
- 重现开始战斗12-编程之美-找符合条件的整数
- [编程之美] PSet2.8 找符合条件的整数
- 【编程之美】2.8 找符合条件的整数
- 编程之美---找符合条件的整数
- 读书笔记之编程之美 – 2.8 找符合条件的整数
- 【C编程】找出符合以下条件的Troitsky数,将该数的首位数字移动到末位数字之后得到的数是原数的整数倍
- 编程之美-2.8 找到符合条件的整数
- 编程之美-2.8-找符合条件的整数
- 编程之美——找符合条件的整数
- shell脚本编程之条件判断,算术运算,整数测试及特殊变量
- 找符合条件的整数
- 《编程之美》找符合条件的整数之C语言实现
- 找出符合以下条件的Troitsky数,将该数的首位数字移动到末位数字之后得到的数是原数的整数倍 例如:将142857的首位数字1移动到末位之后得到的数是428571,而428571=3*1
- C语言编程练习——查找介于n1与n2(0<n1<n2<32768)之间所有满足下列条件的整数:
- 0711编程之美找符合条件的整数
- 输入一个整数,如果该整数x符合条件:-1<x<10,则输出“ok”,否则输出“not ok”
- 找符合条件的整数
- Linux下的C语言编程——查找介于n1与n2(0<n1<n2<32768)之间所有满足下列条件的整数