11

每个正整数除以表示(以 10 为底)仅包含零和一的数字。

可以证明:

考虑数字 1、11、111、1111 等直到 111...1,其中最后一个数字有 n+1 位。称这些数字为 m 1 , m 2 , ... , m n+1。每个除以 n 时都有一个余数,其中两个余数必须相同。因为其中有 n+1 个,但余数只能取 n 个值。这是著名且有用的“鸽巢原理”的应用;

假设余数相同的两个数是 m i和 m j ,其中 i < j。现在从较大的减去较小的。结果数 m i -m j由 j - i 个 1 后跟 i 个零组成,必须是 n 的倍数。

但是如何找到最小的答案呢?并且有效?

4

11 回答 11

6

该问题等于使用 10 i mod n(对于每个 i,最多可以使用一次)来获得 n 的总和 m。这就像一个背包问题或子集和问题。这样,动态编程将完成任务。

在动态规划中,复杂度是O(k*n)。k 是答案的位数。对于 n<10 5,此代码完美运行。

代码:

#include <stdio.h>
#define NUM 2000

int main(int argc, char* argv[])
{
    signed long pow[NUM],val[NUM],x,num,ten;
    int i,j,count;
    for(num=2; num<NUM; num++)
    {
        for(i=0; i<NUM; pow[i++]=0);
        count=0;
        for(ten=1,x=1; x<NUM; x++)
        {
            val[x]=ten;

            for(j=0; j<NUM; j++)if(pow[j]&&!pow[(j+ten)%num]&&pow[j]!=x)pow[(j+ten)%num]=x;
            if(!pow[ten])pow[ten]=x;
            ten=(10*ten)%num;
            if(pow[0])break;
        }

        x=num;
        printf("%ld\tdivides\t",x=num);
        if(pow[0])
        {
            while(x)
            {
                while(--count>pow[x%num]-1)printf("0");
                count=pow[x%num]-1;
                printf("1");
                x=(num+x-val[pow[x%num]])%num;
            }
            while(count-->0)printf("0");
        }
        printf("\n");
    }
}

PS:OEIS中的这个序列。

于 2013-05-11T16:10:29.463 回答
5

有一个 O(n) 时间(算术运算 mod n,真的)解决方案,它比当前接受的答案更有效。这个想法是在顶点 0..n-1 上构建一个图,其中顶点 i 与 (i*10)%n 和 (i*10+1)%n 有连接,然后使用广度优先搜索来查找字典顺序上最少的从 1 到 0 的路径。

def smallest(n):
    parents = {}
    queue = [(1 % n, 1, None)]
    i = 0
    while i < len(queue):
        residue, digit, parent = queue[i]
        i += 1
        if residue in parents:
            continue
        if residue == 0:
            answer = []
            while True:
                answer.append(str(digit))
                if parent is None:
                    answer.reverse()
                    return ''.join(answer)
                digit, parent = parents[parent]
        parents[residue] = (digit, parent)
        for digit in (0, 1):
            queue.append(((residue * 10 + digit) % n, digit, residue))
    return None
于 2015-03-27T11:50:17.273 回答
5

好问题。我使用 BFS 通过中间相遇和其他一些修剪来解决这个问题。现在我的代码可以在合理的时间内解决 n<10 9 。

#include <cstdio>
#include <cstring>

class BIT {
private: int x[40000000];
public:
    void clear() {memset(x, 0, sizeof(x));}
    void setz(int p, int z) {x[p>>5]=z?(x[p>>5]|(1<<(p&31))):(x[p>>5]&~(1<<(p&31)));}
    int bit(int p) {return x[p>>5]>>(p&31)&1;}
} bp, bq;

class UNIT {
private: int x[3];
public: int len, sum;
    void setz(int z) {x[len>>5]=z?(x[len>>5]|(1<<(len&31))):(x[len>>5]&~(1<<(len&31)));}
    int bit(int p) {return x[p>>5]>>(p&31)&1;}
} u;

class MYQUEUE {
private: UNIT x[5000000]; int h, t;
public:
    void clear() {h = t = 0;}
    bool empty() {return h == t;}
    UNIT front() {return x[h];}
    void pop() {h = (h + 1) % 5000000;}
    void push(UNIT tp) {x[t] = tp; t = (t + 1) % 5000000;}
} p, q;

int n, md[100];

void bfs()
{
    for (int i = 0, tp = 1; i < 200; i++) tp = 10LL * (md[i] = tp) % n;

    u.len = -1; u.sum = 0; q.clear(); q.push(u); bq.clear();
    while (1)
    {
        u = q.front(); if (u.len >= 40) break; q.pop();
        u.len++; u.setz(0); q.push(u);
        u.setz(1); u.sum = (u.sum + md[u.len]) % n;
        if (!bq.bit(u.sum)) {bq.setz(u.sum, 1); q.push(u);}
        if (!u.sum) {
            for (int k = u.len; k >= 0; k--) printf("%d", u.bit(k));
            puts(""); return;
        }
    }

    u.len = 40; u.sum = 0; p.clear(); p.push(u); bp.clear();
    while (1)
    {
        u = p.front(); p.pop();
        u.len++; u.setz(0); p.push(u);
        u.setz(1); u.sum = (u.sum + md[u.len]) % n;
        if (!bp.bit(u.sum)) {bp.setz(u.sum, 1); p.push(u);}
        int bf = (n - u.sum) % n;
        if (bq.bit(bf)) {
            for (int k = u.len; k > 40; k--) printf("%d", u.bit(k));
            while (!q.empty())
            {
                u = q.front(); if (u.sum == bf) break; q.pop();
            }
            for (int k = 40; k >= 0; k--) printf("%d", u.bit(k));
            puts(""); return;
        }
    }
}

int main(void)
{
    // 0 < n < 10^9
    while (~scanf("%d", &n)) bfs();
    return 0;
}
于 2013-05-22T00:57:39.283 回答
2

我认为这是一个公平而有趣的问题。

请注意,尽管您所描述的是证明总是存在这样的数字,但找到的数字并不总是最小的。

我能想到的唯一解决方案是计算给定的 10 模的幂的余数,n然后尝试使用这些幂中的最多一个来构造一个给定余数 0 模的和。你永远不需要超过 n 个不同的权力(你证明了你的问题)。

于 2013-05-09T09:07:24.500 回答
2

这是readable在 java 中使用 BFS 的解决方案。该方法与 David 的方法相似,但有一些改进。

您构建一个决策树,决定是否附加 0 或 1,并执行 BFS 以找到输入数字的最低有效倍数。

该解决方案还利用模(输入数字)来计算非常大的结果。代码中的注释中提供了完整的描述。

您还可以在ideone中访问相同的代码片段。

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

public class Main {
    // Return the smallest multiple of the number (as a string) consisting only of digits 0 and 1
    //
    // All possible digits that can be constructed using the digits 0/1 can be represented
    // as a tree, where at each level, appending a 0 is one branch and appending a 1 is another
    //
    // If we perform BFS on this tree, the first number we see which is an exact multiple of the input
    // number will be the result (since it will be the smallest). Make sure to consider left
    // branch (i.e. 0) before considering the right branch (i.e. 1)
    //
    // The 2 paths we take at each level when the current number is num:
    //      (num * 10)
    //      (num * 10) + 1
    // 
    // Since the result can grow huge quite easily, it might not be possible to store the result in a
    // 32 or even a 64 bit int/long variable.
    //
    // One alternative is to use BigNumber, but a simpler alternative exists if we leverage modulo.
    //
    // The operations we perform above (i.e. multiplications and additions) will retain the useful part
    // of the result when using modulo. We use the given number itself as the modulo, and when we see a
    // result of 0, it means we have found a number which is an exact multiple of the input number.
    //
    // To reconstruct the number, we need to store the parent nodes of each node, when adding the node
    // in the queue (similar to using BFS for computing shortest path)
    //
    // We will also need to know if we appended a 0 or a 1 at each step, and so we add this information
    // as part of the node data structure as well.
    //
    // Re-visiting nodes is unecessary since we have seen this modulo result (i.e. value % num) already.
    // Any additional digits we add from now will only make the number longer and we already are tracking
    // the path for this same modulo result we've seen earlier.
    //
    public static String multiple(int num) {
        if (num < 0) {
            throw new IllegalArgumentException("Invalid args");
        }

        String result = "0";

        if (num > 0) {
            // An array to mark all the visited nodes
            boolean[] isVisited = new boolean[num];
            Arrays.fill(isVisited, false);

            // The queue used by BFS
            Queue<Node> queue = new ArrayDeque<>();

            // Add the first number 1 and mark it visited
            queue.add(new Node(true, 1 % num, null));
            isVisited[1 % num] = true;

            // The final destination node which represents the answer
            Node destNode = null;

            while (!queue.isEmpty()) {
                // Get the next node from the queue
                Node currNode = queue.remove();

                if (currNode.val == 0) {
                    // We have reached a valid multiple of num
                    destNode = currNode;
                    break;
                } else {
                    // Visit the next 2 neighbors
                    // Append 0 - (currNode.val * 10)
                    // Append 1 - (currNode.val * 10) + 1

                    // Append a '0'
                    int val1 = (currNode.val * 10) % num;
                    if (!isVisited[val1]) {
                        queue.add(new Node(false, val1, currNode));
                        isVisited[val1] = true;
                    }

                    // Append a '1'
                    int val2 = (val1 + 1) % num;
                    if (!isVisited[val2]) {
                        queue.add(new Node(true, val2, currNode));
                        isVisited[val2] = true;
                    }
                }
            }

            // Trace the path from destination to source
            if (destNode == null) {
                throw new IllegalStateException("Result should not be null");
            } else {
                StringBuilder reverseResultBuilder = new StringBuilder();
                Node currNode = destNode;
                while (currNode != null) {
                    reverseResultBuilder.append(currNode.isDigitOne ? '1' : '0');
                    currNode = currNode.parent;
                }
                result = reverseResultBuilder.reverse().toString();
            }
        }

        return result;
    }

    // Node represents every digit being appended in the decision tree
    private static class Node {
        // True if '1', false otherwise (i.e. '0')
        public final boolean isDigitOne;
        // The number represented in the tree modulo the input number
        public final int val;
        // The parent node in the tree
        public final Node parent;

        public Node(boolean isDigitOne, int val, Node parent) {
            this.isDigitOne = isDigitOne;
            this.val = val;
            this.parent = parent;
        }
    }

    public static void main(String[] args) {
        int num = new Scanner(System.in).nextInt();
        System.out.println("Input number: " + num);
        System.out.println("Smallest multiple using only 0s and 1s as digits: " + Main.multiple(num));
    }
}
于 2016-04-27T01:03:41.677 回答
1

这是获得前 792 个答案的快速方法。Def最简单的代码:

__author__ = 'robert'

from itertools import product

def get_nums(max_length):
    assert max_length < 21 #Otherwise there will be over 2 million possibilities
    for length in range(1, max_length + 1):
        for prod in product("10", repeat=length):
            if prod[0] == '1':
                yield int("".join(prod))

print list(get_nums(4))
[1, 11, 10, 111, 110, 101, 100, 1111, 1110, 1101, 1100, 1011, 1010, 1001, 1000]

nums = sorted(get_nums(20))
print len(nums)

solution = {}

operations = 0

for factor in range(1, 1000):
    for num in nums:
        operations += 1
        if num % factor == 0:
            solution[factor] = num
            break
    print factor, operations
    if factor not in solution:
        print "no solution for factor %s" % factor
        break

print solution[787]

max_v = max(solution.values())
for factor, val in solution.items():
    if val == max_v:
        print factor, max_v


[1, 11, 10, 111, 110, 101, 100, 1111, 1110, 1101, 1100, 1011, 1010, 1001, 1000]
1048575
1 1
2 3
3 10
4 14
5 16
6 30
7 39
8 47
9 558
10 560
11 563
12 591
13 600
14 618
15 632
16 648
17 677
18 1699
19 1724
20 1728
..
..
187 319781
188 319857
..
..
791 4899691
792 5948266
no solution for factor 792
10110001111
396 11111111111111111100
于 2013-05-22T01:45:36.917 回答
1

这是使用链表的 C# 解决方案

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        public static void print(LinkedList<int> lst)
        {
            foreach(int i in lst)
            {
                Console.Write(i);
            }
        }

        static void Main(string[] args)
        {
            int number = Convert.ToInt32(Console.ReadLine());
            int product;
            LinkedList<int> list = new LinkedList<int>();
            bool Istrue = true;
            int range = 1;

            while (range <= 10) { 
                Istrue = true;
                product = number * range;
                while (product > 0)
                {
                    list.AddFirst(product % 10);
                    product /= 10;

                }

                foreach (int i in list)
                {
                    if (i > 1) Istrue = false;
                }

                if (Istrue) { print(list); break; }
                else
                {
                    list.Clear();   
                }

                range++;    
            }

            Console.WriteLine("Done");

            string s = Console.ReadLine();
        }
    }
}
于 2015-03-28T18:35:13.800 回答
0

我的算法将是:-

1)构造n个可能数字的排序树(比如n最初是10)。所以在这个例子中它将包含 1,10,11,100,101,110,111....

2)然后循环遍历列表并在每个 no x%GivenNo 上执行,如果它是最小的可能 no

3) 否则用另外 10 个数字重复步骤 3

于 2016-07-03T10:01:44.667 回答
0

这是 Raku 中的蛮力版本:

say (1..Inf).map( *.base(2) ).first( * %% $n );

该代码生成一个惰性(可能无限)候选数字序列,然后搜索first可被 n 整除的元素。

作为蛮力,它并不是特别快,但代码在其简单性和表现力方面引人注目,因为它是 Raku 的典型特征。

于 2020-02-25T21:10:22.987 回答
0
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Class1
    {
        public static void Main()
        {

            List<string> possibleCombination = new List<string>();

            for (int i = 2; i < 10000; i++)
            {
                possibleCombination.Add(Convert.ToString(i, 2));
            }

            var input = Console.ReadLine();



            long output = 0;

            foreach (var item in possibleCombination)
            {
                if (Convert.ToInt64(item) % Convert.ToInt64(i) == 0)
                {
                    output = Convert.ToInt64(item);
                    break;
                }
            }

            Console.WriteLine(output);

            Console.ReadLine();
        }

    }
}
于 2016-09-07T14:11:23.463 回答
0

这是使用图形和 bfs 方法的 O(n) 中的完整 c# 代码。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Security.Cryptography;
using System.Linq;
using System.Runtime.InteropServices;

class Solution {
    public class Edge : IComparable
    {
        public int From
        {
            get;
            set;
        }
        public int To
        {
            get;
            set;
        }
        public int Weight
        {
            get;
            set;
        }
        public bool IsDirected
        {
            get;
            set;
        }

        public Edge(int from, int to, bool isDirected = false, int weight = 0)
        {
            this.From = from;
            this.To = to;
            this.Weight = weight;
            this.IsDirected = isDirected;
        }

        public int CompareTo(object obj)
        {
            if (obj is Edge)
            {
                var comparingTo = obj as Edge;
                return this.Weight.CompareTo(comparingTo.Weight);
            }
            return 0; //TODO:what should we return?
        }
    }

    public class AdjNode
    {
        public int EdgeWeight
        {
            get;
            set;
        }

        public int Id
        {
            get;
            set;
        }

        public AdjNode(int id)
        {
            this.Id = id;
            this.EdgeWeight = 0;
        }
        public AdjNode(int id, int weight)
        {
            this.Id = id;
            this.EdgeWeight = weight;
        }
    }
    public class GraphAdj
    {

        public int V
        {
            get;
            set;
        }

        public List<AdjNode>[] adj
        {
            get;
            set;
        }

        public List<Edge> Edges
        {
            get;
            set;
        }

        public GraphAdj(int v)
        {
            this.V = v;
            this.adj = new List<AdjNode>[this.V];
            for (int i = 0; i < this.V; i++)
            {
                this.adj[i] = new List<AdjNode>(); //allocate actual memory
            }
            this.Edges = new List<Edge>();
        }

        public void AddDirectedEdge(int from, int to)
        {
            adj[from].Add(new AdjNode(to));
            this.Edges.Add(new Edge(from,to,true));
        }

        public void AddDirectedEdge(int from, int to, int weight)
        {
            adj[from].Add(new AdjNode(to,weight));
            this.Edges.Add(new Edge(from, to, true, weight));
        }
    }
    public string multiple(int A) {
        int n = A;
        GraphAdj directedGraphForNumber = new GraphAdj(n);
        Queue<int> queueForNumbers = new Queue<int>();
            string result = String.Empty;
            bool[] visitedForNumbers = new bool[directedGraphForNumber.V];
            int[] suffixes = new int[2] { 0, 1 };
            //we will start from 1st node out of n node

            queueForNumbers.Enqueue(1);
            visitedForNumbers[1] = true;

            while (true)
            {
                int from = queueForNumbers.Dequeue();
                if (from == 0)
                    break;

                for (int i = 0; i < suffixes.Length; i++)
                {
                    int toNode = from * 10 + suffixes[i];
                    int reminder = toNode % n;
                    if (visitedForNumbers[reminder] == false)
                    {
                        visitedForNumbers[reminder] = true;
                        queueForNumbers.Enqueue(reminder);
                        directedGraphForNumber.AddDirectedEdge(from, reminder,suffixes[i]);
                    }
                }
            }

            //Do BFS traversal with edges until zero th node encounters
            bool[] visitedForBfs = new bool[directedGraphForNumber.V];
            Queue<int> queueForBfs = new Queue<int>();
            int[] parent = new int[directedGraphForNumber.V];

            int source = 1;
            visitedForBfs[source] = true;
            queueForBfs.Enqueue(source);
            parent[source] = -1;

            while (queueForBfs.Count > 0)
            {
                int currentVertex = queueForBfs.Dequeue();

                foreach (var adjacentVertex in directedGraphForNumber.adj[currentVertex])
                {
                    if (visitedForBfs[adjacentVertex.Id] == false)
                    {
                        queueForBfs.Enqueue(adjacentVertex.Id);
                        parent[adjacentVertex.Id] = currentVertex;
                        visitedForBfs[adjacentVertex.Id] = true;
                    }
                    if (adjacentVertex.Id == 0) // we reach zero th node
                    {
                        queueForBfs.Clear(); //break out of bfs
                    }
                }
            }

            //now time to find path all the way to start from zero using parent
            List<int> pathListUsingParent = new List<int>();
            int current = 0;
            pathListUsingParent.Add(0); // add zero

            while (current!=1)
            {
                pathListUsingParent.Add(parent[current]);
                current = parent[current];
            }

            //reverse path to make number using edges
            pathListUsingParent.Reverse();

            result += "1"; //start node

            //now read edges
            for (int i = 0; i < pathListUsingParent.Count-1; i++)
            {
                int from = pathListUsingParent[i];
                int to = pathListUsingParent[i + 1];

                result += directedGraphForNumber.adj[from].FirstOrDefault(adj => adj.Id == to).EdgeWeight;
            }

            return result;
    }
}
于 2017-01-26T18:00:59.220 回答