1·; = N< = 1000
如何找到可被N整除的最小正数,其数字和应该等于N.
例如:
N:结果
1:1个
10:190
算法不应超过2秒。
任何想法(Pseudocode,pascal,c ++或java)?
答案 0 :(得分:1)
设f(len,sum,mod)为bool,这意味着我们可以构建一个数字(可能带有前导零),长度为len + 1,数字总和等于sum,并在潜水时给出mod。
然后f(len,sum,mod)=或(f(len-1,sum-i,mod- i * 10 ^ len),对于i从0到9)。然后你可以找到最小的l,即f(l,n,n)为真。之后只需找到第一个数字,然后是第二个数字,依此类推。
#define FOR(i, a, b) for(int i = a; i < b; ++i)
#define REP(i, N) FOR(i, 0, N)
#define FILL(a,v) memset(a,v,sizeof(a))
const int maxlen = 120;
const int maxn = 1000;
int st[maxlen];
int n;
bool can[maxlen][maxn+1][maxn+1];
bool was[maxlen][maxn+1][maxn+1];
bool f(int l, int s, int m)
{
m = m%n;
if(m<0)
m += n;
if(s == 0)
return (m == 0);
if(s<0)
return false;
if(l<0)
return false;
if(was[l][s][m])
return can[l][s][m];
was[l][s][m] = true;
can[l][s][m] = false;
REP(i,10)
if(f(l-1, s-i, m - st[l]*i))
{
can[l][s][m] = true;
return true;
}
return false;
}
string build(int l, int s, int m)
{
if(l<0)
return "";
m = m%n;
if(m<0)
m += n;
REP(i,10)
if(f(l-1, s-i, m - st[l]*i))
{
return char('0'+i) + build(l-1, s-i, m - st[l]*i);
}
return "";
}
int main(int argc, char** argv)
{
ios_base::sync_with_stdio(false);
cin>>n;
FILL(was, false);
st[0] = 1;
FOR(i, 1, maxlen)
st[i] = (st[i-1]*10)%n;
int l = -1;
REP(i, maxlen)
if(f(i, n, n))
{
cout<<build(i,n,n)<<endl;
break;
}
return 0;
}
注意,它使用~250 MB的内存。
编辑:我找到了这个解决方案运行的测试,有点太长了。 999,差不多5秒。
答案 1 :(得分:0)
更新:我知道结果应该在0到1000之间,但不是。对于较大的输入,天真的算法可能需要相当长的时间。 80的输出将是29999998880。
您不需要花哨的算法。在任何合理的现代计算机上,即使在解释型语言中,检查1000个数字条件的循环也只需不到2秒。
如果你想让它变得聪明,你只需要检查N倍数的数字。为了进一步限制搜索空间,N的余数和结果在除以9时必须相等。这意味着现在你必须每9N检查一个号码。
答案 2 :(得分:0)
当然,伪代码,因为它闻起来像家庭作业: - )
def findNum (n):
testnum = n
while testnum <= 1000:
tempnum = testnum
sum = 0
while tempnum > 0:
sum = sum + (tempnum mod 10)
tempnum = int (tempnum / 10)
if sum == n:
return testnum
testnum = testnum + n
return -1
在你的两秒门槛下翻译成Python时需要大约15千分之一秒。它的工作原理是基本上测试N
的每个倍数小于或等于1000。
测试遍历数字中的每个数字,然后将其添加到总和中,如果该总和与N
匹配,则返回数字。如果否编号符合条件,则返回-1。
作为测试用例,我使用了:
n findNum(n) Justification
== ========== =============
1 1 1 = 1 * 1, 1 = 1
10 190 190 = 19 * 10, 10 = 1 + 9 + 0
13 247 247 = 13 * 19, 13 = 2 + 4 + 7
17 476 476 = 17 * 28, 17 = 4 + 7 + 6
99 -1 none needed
现在只检查倍数高达1000而不是检查所有数字,但检查所有数字开始花费的时间超过两秒,无论您使用何种语言。您可能能够找到更快的算法,但我想提出其他建议。
您可能找不到比简单查找表中的值所需的算法更快的算法。所以,我只需运行一个程序一次来生成输出:
int numberDesired[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
190, 209, 48, 247, 266, 195, 448, 476, 198, 874,
...
-1, -1};
然后将其插入新程序,以便它可以使用快速查找。
例如,您可以使用某些Python来执行此操作:
print "int numberDesired[] = {"
for i in range (0, 10):
s = " /* %4d-%4d */"%(i*10,i*10+9)
for j in range (0, 10):
s = "%s %d,"%(s,findNum(i*10+j))
print s
print "};"
生成:
int numberDesired[] = {
/* 0- 9 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
/* 10- 19 */ 190, 209, 48, 247, 266, 195, 448, 476, 198, 874,
/* 20- 29 */ 3980, 399, 2398, 1679, 888, 4975, 1898, 999, 7588, 4988,
/* 30- 39 */ 39990, 8959, 17888, 42999, 28798, 57995, 29988, 37999, 59888, 49998,
/* 40- 49 */ 699880, 177899, 88998, 99889, 479996, 499995, 589996, 686999, 699888, 788998,
/* 50- 59 */ 9999950, 889899, 1989988, 2989889, 1999998, 60989995, 7979888, 5899899, 8988898, 8888999,
/* 60- 69 */ 79999980, 9998998, 19999898, 19899999, 59989888, 69999995, 67999998, 58999999, 99899888, 79899999,
:
};
这需要花费超过两秒的时间,但事情就是这样:你只需运行一次,然后将表格剪切并粘贴到代码中。一旦你有了表格,它很可能会破坏任何算法解决方案。
答案 3 :(得分:0)
您需要担心的最大数字总和是1000
。由于1000 / 9 = ~100
这实际上并不多,所以我认为以下内容应该有效:
考虑以下数据结构:
entry { int r, sum, prev, lastDigit; }
保留entry
的队列,最初有r = 1 mod N, sum = 1, prev = -1, lastDigit = 1; r = 2 mod N, sum = 2, prev = -1, lastDigit = 2
等。
从队列中提取条目x
时:
y = new entry
for i = 0 to 9 do
y.r = (x.r * 10 + i) % N
y.sum = x.sum + i
y.prev = <position of x in the queue>
y.lastDigit = i
if y.r == 0 and y.sum == N
// you found your multiple: use the prev and lastDigit entries to rebuild it
if y.sum < N then
queue.add(y)
这基本上是数字上的BFS。由于你关心的最大金额很小,这应该非常有效。
答案 4 :(得分:0)
在考虑了一下之后,我想我找到了预期的答案。
将其视为图表。对于任何数字,您可以通过将该数字乘以10并添加任何数字0-9来创建新数字。您需要先使用BFS来达到最小的数字。
对于每个节点保持总和和余数。使用这些值可以移动到相邻节点,这些值也可以帮助您避免一次又一次地达到无用状态。要打印该数字,您可以使用这些值来跟踪您的步骤。 复杂度为O(n ^ 2),在最坏的情况下,表格完全填满。 (见代码)
注意:代码首先需要测试用例数。对于n <= 1000,工作在0.3s以下。
[编辑] :在6.54秒的时间内使用。测试文件有50个数字。
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define F first
#define S second
#define N 1100
#define mp make_pair
queue<pair<int, int> >Q;
short sumTrace[N][N], mulTrace[N][N];
void print(int sum, int mul) {
if (sumTrace[sum][mul] == 42)return;
print(sum-sumTrace[sum][mul], mulTrace[sum][mul]);
printf("%d",sumTrace[sum][mul]);
}
void solve(int n) {
Q.push(mp(0,0));
sumTrace[0][0]=42; // any number greater than 9
while (1) {
int sum = Q.front().F;
int mul = Q.front().S;
if (sum == n && mul == 0) break;
Q.pop();
for (int i=0; i<10; i++) {
int nsum = sum+i;
if (nsum > n)break;
int nmul = (mul*10+i)%n;
if (sumTrace[nsum][nmul] == -1) {
Q.push(mp(nsum, nmul));
sumTrace[nsum][nmul] = i;
mulTrace[nsum][nmul] = mul;
}
}
}
print(n,0);
while(!Q.empty())Q.pop();
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
memset(sumTrace, -1, sizeof sumTrace);
solve(n);
printf("\n");
}
return 0;
}