每个正整数除以某个数字,其表示(基数10)仅包含零和1。
可以证明:
考虑数字1,11,111,1111等,直到111 ... 1,其中 最后一个数字有n + 1位数。将这些数字称为m 1 ,m 2 ,...,m n + 1 。每个都有一个 余数除以n,其中两个余数必须相同。 因为它们中有n + 1个,但只剩下n个值。 这是着名而有用的“鸽笼原理”的应用;
假设具有相同余数的两个数字是m i 且m j ,与i<学家现在从较大的减去较小的。得到的数字m i -m j ,由j-i个后跟i零组成,必须是n的倍数。
但如何找到最小的答案?而且效率很高?
答案 0 :(得分: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。
答案 1 :(得分:6)
有一个O(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
答案 2 :(得分:4)
好问题。我使用BFS通过中间会合和其他一些修剪来解决这个问题。现在我的代码可以在合理的时间内解决n&lt; 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;
}
答案 3 :(得分:3)
这是在java中使用BFS的readable
解决方案。这种方法类似于大卫的改进。
您构建了一个决策树,其中是否附加0或1并执行BFS以查找输入数字的最低有效倍数。
此解决方案还利用modulo(输入数字)来计算非常大的结果。代码中的注释中提供了完整描述。
您还可以在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));
}
}
答案 4 :(得分:2)
我认为这是一个公平有趣的问题。
请注意,尽管您所描述的是证据总是存在这样的数字,但找到的数字并不总是最小的。
我能想到的唯一解决方案是计算给定n
的10模的幂的余数,并尝试构造一个和,使用这些幂中的每一个中的至少一个来给出余数0模n。你永远不会需要超过n种不同的权力(你证明了你的问题)。
答案 5 :(得分:1)
这是获得前792个答案的快捷方式。找出最简单的代码:
__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
答案 6 :(得分: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();
}
}
}
答案 7 :(得分:0)
我的算法将是: -
1)构造n个可能数字的有序树(比如n最初是10)。所以在这个例子中它将包含1,10,11,100,101,110,111 ....
2)然后遍历列表并执行每个没有x%GivenNo,如果它的o尽可能小
3)否则用另外10个数字重复步骤3
答案 8 :(得分: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();
}
}
}
答案 9 :(得分:0)
以下是使用graph和bfs方法的O(n)中的完整c#代码。
E
答案 10 :(得分:0)
这是Raku中的蛮力版本:
say (1..Inf).map( *.base(2) ).first( * %% $n );
该代码生成一个候选数的惰性(可能无限)序列,然后搜索可被n整除的first
元素。
由于是蛮力的,它并不是特别快,但是代码的简单性和表现力令人震惊,这是Raku的典型特征。