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


任意给定一个正整数N,求一个最小的正整数M(M >1),使得N * M 的十进制表示形式里只含有1 和0,例如:
1 * 1 = 1 2 * 5 = 10
3 * 37 = 111 4 * 25 = 100
5 * 2 = 10 6 * 185 = 1,110
7 * 143 = 1,001 8 * 125 = 1,000
9 * 12,345,679 = 111,111,111
http://blog.csdn.net/jcwkyl/article/details/3859155
方法四:对方法三的改进。将方法三的搜索空间按模N余数分类,使得搜索时间和空间都由原来的指数级降到了O(N)。改进的原理:假设当前正在搜索由0,1组成的K位十进制数,这样的K位十进制数共有2^k个。假设其中有两个数X、Y,它们模N同余,那么在搜索由0、1组成的K+1位十进制数时,X和Y会被扩展出四个数:10X, 10X+1, 10Y, 10Y+1。因为X和Y同余(同余完全可以看作相等),所以10X与10Y同余,10X+1与10Y+1同余。也就是说由Y扩展出来的子树和由X扩展产生出来的子树产生完全相同的余数,如果X比Y小,那么Y肯定不是满足要求的最小的数,所以Y这棵子树可以被剪掉。这样,2^K个数按照模N余数分类,每类中只保留最小的那个数以供扩展。原来在这一层需要搜索2^K个数,现在只需要搜索O(N)个数。例如,当N=9时,第0层是1(1),

如上图所示,第2层的110,第三层的1010、1110都因为同一层有和它同余且更小的数而被剪掉。如果按照方法三搜索,第三层本来应该有8个结点,但现在只有4个结点。
  1. // 解法四:将搜索空间分过类的广度搜索,这样空间占用是O(N)而不是  
  2. // 指数级。分类的原则是按照模N的余数分类
  3. struct QNode {  
  4.     int v, r; // v is value, r is remainder  
  5.     QNode(int vv, int rr): v(vv), r(rr) {}  
  6.     QNode(): v(0), r(0) {}  
  7. };  
  8. int main() {  
  9.     int N;  
  10.     while(scanf("%d", &N) != EOF) {  
  11.         //bitset<N> bn;  
  12.         queue<QNode> q;  
  13.         q.push(QNode(1, 1));  
  14.         while(!q.empty()) {  
  15.             //bn.reset();  
  16.             vector<bool> bn(N, false);  
  17.             int s = q.size();  
  18.             while(s--) {  
  19.                 QNode t = q.front();  
  20.                 if(t.r == 0) {  
  21.                     printf("n = %d, m = %d, n*m = %d/n", N, t.v/N, t.v);  
  22.                     goto ok;  
  23.                 }  
  24.                 q.pop();  
  25.                 if(!bn[t.r * 10 % N]) {  
  26.                     bn[t.r * 10 % N] = true;  
  27.                     q.push(QNode(t.v * 10, t.r * 10 % N));  
  28.                 }  
  29.                 if(!bn[(t.r * 10 + 1) % N]) {  
  30.                     bn[(t.r * 10 + 1) % N] = true;  
  31.                     q.push(QNode(t.v * 10 + 1, (t.r * 10 + 1) % N));  
  32.                 }  
  33.             }  
  34.         }  
  35. ok:;  
  36.     }  
  37.     return 0;  
  38. }  


方法三:因为N*M的取值就是1,10,11,100,101,110,111,......所以直接在这个空间搜索,这是对方法一的改进。搜索这个序列直到找到一个能被N整除的数,它就是N*M,然后可计算出M。例如N=3时,搜索树如下:

上图中括号内表示模3的余数。括号外表示被搜索的数。左子树表示0,右子树表示1.上图中搜索到第二层(根是第0层)时遇到111,它模3余数为0.所以N*M=111, M=111/3=37。
  1. // 解法三(1):广度优先搜索  
  2. int main() {  
  3.     int N;  
  4.     while(scanf("%d", &N) != EOF) {  
  5.         queue<int> q;  
  6.         q.push(1);  
  7.         while(!q.empty()) {  
  8.             int t = q.front();  
  9.             q.pop();  
  10.             if(t % N == 0) {  
  11.                 printf("n = %d, m = %d, n*m = %d/n", N, t/N, t);  
  12.                 break;  
  13.             }  
  14.             q.push(t * 10);  
  15.             q.push(t * 10 + 1);  
  16.         }  
  17.     }  
  18.     return 0;  
  19. }  
方法一:给定N,令M从2开始,枚举M的值直到遇到一个M使得N*M的十进制表示中只有1和0.
  1. int HasOnlyOneAndZero(unsigned int n) {  
  2.     while(n) {  
  3.         if(n % 10 >= 2) return 0;  
  4.         n /= 10;  
  5.     }  
  6.     return 1;  
  7. }  
  8. int main() {  
  9.     int n, m;  
  10.     while(scanf("%d", &n) != EOF) {  
  11.         for(m = 1;;m++) {  
  12.             if(HasOnlyOneAndZero(n*m)) {  
  13.                 printf("n = %d, m = %d, n*m = %d/n", n, m, n*m);  
  14.                 break;  
  15.             }  
  16.         }  
  17.     }  
  18.     return 0;  
  19. }  
方法二:求出10的次方序列模N的余数序列并找出循环节。然后搜索这个余数序列,搜索的目的就是要在这个余数序列中找到一些数出来让它们的和是N的倍数。例如N=13,这个序列就是1,10,9,12,3,4然后不断循环。很明显有1+12=13,而1是10的0次方,12是10的3次方,所以这个数就是1000+1=1001,M就是1001/13=77。


符合条件整数存在的证明
http://blog.csdn.net/cjl19880906/article/details/8441365
现在要讨论和证明的是存在这样的整数X,使得X%N=0。
因为 数列
      (1)
是一个无穷数列并且数列中的每一项的取值范围都在【0,N-1】。
这样的话,考虑鸽巢原理,肯定存在第 s,t 项使得10^s = 10^t  (mod N)。
再由模运算的性质:
若a≡b (mod p),则对于任意的c,都有ac≡ bc (mod p) 
可以知道数列(1)中存在循环节,节长 s-t 。比如,若是N=13,那么这个数列是1,10,9,12,3,4,1,10,9,12,3,4,1,10,9,12,3,4,1,10,9,12,3,4... ...
于是有
所以
这就找到了一个符合条件的X。因为最终要求最小的一个符合条件的整数,既然存在了一个这样的整数,自然的最小的肯定存在了。

http://gpww.blog.163.com/blog/static/11826816420099782740350/\

解法二


    我们也可以反过来考虑,去遍历乘数,例如,当N=13 时,采用“试乘”的办法去得到M——13 的个位数是3,能够让3 变成1 的数只有7(3×7=21),依次类推:
image
    需要注意的是,个位不可以“试乘”0,因为这样得到的M 不是最小的(还可以缩小10 倍),其它高位都可以“试乘0。
    另外,还可能出现无限循环的“试乘”:
image
    当N 为d 位数的时候,“试乘”可能会出现d 位循环出现,需要排除,关键代码如下:
        void Find_01_Format(BigInt x, BigInt y, BigInt z, int digit, Dictionary<string> cycleDic)
        {
            for (int m = digit > 0 ? 0 : 1; m < 10; m++)
            {
                var sum = m * x[0] + z[digit];
                var u = sum % 10;
                if (u == 1 || u == 0)//找到一个能将最后乘积digit 位变为0 或1 的数
                  {
                    y[digit] = m;
                    var p = (x * m).LeftShift(digit).Add(z);
                    if (min.Digit > 0 && p >= min) continue;//大于当前最小值,剪枝
                       //<检查是否找到结果> <检查是否出现循环>
                    Find_01_Format(x, y.Clone(), p, digit + 1, copyDic);
                }
            }
        }
其中:
x 是被乘数,y 是乘数,z 是乘积
digit 是当前“试乘”的位数
cycleDic 是用于判断是否出现循环的哈希表
BigInt. LeftShift(k)是将10 进制数左移k 位,等价于×10k
BigInt.Add(n)是将本数字加上n 后返回自身。
min 保存的当前搜索到的最小的z
 

解法三


    当N 比较大的时候,两种方法都比较慢,例如N=987654 时:987654 * 1,113,861,738,118,815 = 1,100,110,001,100,000,110,010
    能否在秒级解决10 万以下的数呢?
    可以这样考虑,我们要得到的乘积只包含0 和1,即:
    z = b1 * 10^0 + b2 * 10^1 + b3 * 10^2 + … + bk * 10^k-1
    其中,bi = 0 或1,i = 1,2,3,…,k
    考虑无穷数列:10^0 % N,10^1 % N,10^2 % N,…,由于N 的余数总是< N,所以这个数列必将存在循环节,例如,当N = 13 时,此数列为:
1, 10, 9, 12, 3, 4, 1, 10, 9, 12, 3, 4, 1, 10, 9, 12, 3, 4, 1, 10…
    现在问题就可以转化为:在这个无穷数列中,挑选一些数出来,使得他们的和能够整除N,一种挑选方案实际上对应于b1 b2 b3…的一种真值指派(每一位选中为1,否则为0)。
   
    到此我们已经发现,这个问题可以采用“分级组合法”:
    给定无穷序列100 % N,101 % N,102 % N,…求1~n 级的情况。第i 级保存序列中i 个数相加所能得到和,直到找到最小的一个和能够整除N。
    代码如下:
           var heap = new List<SumSet>(); // SumSet 类保存数列中n 个数相加所能得到的和
             heap.Add(new SumSet());
            heap[0].Add(0);
            int d = 1, b = 1, s = 0, cycleStart = -1;
            SumSet.Sum min = null;
            while (true)
            {
                var r = b % n;
                //找到循环节开始的那个元素
                 if (cycleStart < 0 && heap.Count > 1 && heap[1].ContainsKey(r)) cycleStart = r;
               if (r == cycleStart) s++; //进入下一个循环周期,前s 级不需要再扩展
                 bool found = false;
                heap.Add(new SumSet());
                for (int lvl = d - 1; lvl >= s; lvl--)
                    foreach (var sum in heap[lvl])
                    {
                        var newSum = sum.Add(r, d - 1);
                        if (newSum.value % n == 0)
                        {
                            found = true;
                            if (min == null || newSum < min) min = newSum;
                        }
                        heap[lvl + 1].Renew(newSum);
                    }
                if (found) break;
                b *= 10 % n;
                b %= n;
                d++;
            }
 
其中:
n 是给定的数字N
d 表示当前计算到无穷数列的哪一位
b 依次是10^0,10^1,10^2,10^3…,为了防止越界,计算过程中先求余,结果不变。

讨论


    通过“分级组合法”基本能够秒级解决10 万以下的数字了,逐数测试时,相比前面两法“卡卡”,真可谓酣畅淋漓!
    由于从下往上看每级元素数量列呈现“橄榄型”——中间多两头少,在面对更大的数字的时候,未能到达此数字之前,就被困扰大量计算中,类似“广度优先搜索”。那么在需要计算这么大的数字之前,还可以改进此算法,利用“深度优先搜索”、“启发式搜索”,或者遗传算法给出近似解。
《编程之美》2.8 扩展问题
http://icyxiangzi.blog.163.com/blog/static/169778905201010279208345/
5、扩展问题2
使得N*M只含有0和1,并且N*M<2^16,N*M<=11111,且使得M尽可能的大。首先,用11111%N,观察余数是否为0。如果余数为0,那么11111/N就是所求的M。如果余数不是0,设余数是a,与原题一样,求一个最小的由0和1组成的数b,使得b模N等于a,然后用(11111-b)/N就是所求的M。

http://blog.csdn.net/zhanglei0107/article/details/8277949
http://hi.baidu.com/silverxinger/item/50e87c13259dfceb5f53b1a5


















Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts