我试图找到有多少长度为n的数量,使得每个数字至少比它前后的数字小4个/更大。 例如:如果n = 5,则这些数字为39518,15951等
以下是解决方案,我可以想出: 即使输入大小低至1000,也需要很长时间。我相信,有一些更好的方法可以解决这个问题。如果有人能给出一些指示,我将不胜感激。
#include <stdio.h>
int out[100000];
int count;
void foo(int *out, int pos_to_fil, int size) {
if (pos_to_fil == size) {
count++;
return;
}
int i;
for (i=0;i<=9;i++) {
if (pos_to_fil == 0 && i == 0)
continue;
if (pos_to_fil >0 && abs(out[pos_to_fil-1] -i) < 4)
continue;
out[pos_to_fil] = i;
foo(out, pos_to_fil + 1, size);
}
}
int main(void) {
foo(out, 0, 1000);
printf("count %d\n", count);
return 0;
}
答案 0 :(得分:4)
简短回答:不要使用递归,自下而上使用动态编程。
更长的回答:
基本上,您将迭代所有可能的解决方案。增加计数的唯一声明是count++
,因为我们必须使用超过600位的数字,这需要花费一些时间。 (即使它不会为每个count++
)
所以不知怎的,我们需要增加数量,然后只增加1。怎么做?
假设我们已经知道n = 2的答案是36种可能性。这有助于我们计算n = 3的可能性吗?不,不是真的,因为我们不知道这36个数字是什么。其中一个两位数字是15
,可以扩展为150
,151
和159
(3种可能性)。另一个两位数字是30
,可以扩展为304
,305
,306
,307
,308
和{{1} (6种可能性)。我们显然不能将36乘以一些常数因子来得到n = 3的解。
但是仍有一种模式。 309
为下一代产生6个新数字的事实意味着30
,40
,50
以及以{{1}结尾的所有其他两位数字还会产生6个新数字。 60
会产生3个新数字,所有其他数字也会产生0
。
那么如果我们从计算n = 2开始,而不是记住所有36个数字,我们就会记住这个数组:15
。这个数组意味着我们并不确切地知道这36个数字是什么,但其中6个数字以5
结尾,其中5个以[6, 5, 4, 3, 2, 2, 2, 3, 4, 5]
结尾,4个0
结束等等。
现在我们可以通过做一些添加来计算n = 3的相同数组。可以1
,2
,0
,4
,5
或6
生成7
。将它们全部添加意味着对于n = 3,将存在2 + 2 + 2 + 3 + 4 + 5 = 18个以8
结尾的数字。 n = 3的整个数组是9
不幸的是我不会说c但这里是java的解决方案。
0
它有两个数组:[18, 16, 14, 12, 15, 16, 15, 18, 20, 22]
用于当前代的数组(在上例中n = 2)和import java.util.*;
import java.math.*;
class BigNum {
public static void main (String[] a) {
Scanner in = new Scanner (System.in);
System.out.println (new BigNum().solve(in.nextInt()));
}
BigInteger solve(int n) {
if (n == 0) return BigInteger.ZERO;
BigInteger[] counts = new BigInteger[10];
BigInteger[] next = new BigInteger[10];
BigInteger[] temp;
Arrays.fill (counts, BigInteger.ONE);
counts[0] = BigInteger.ZERO;
for (int i = 1; i < n; i++) {
for (int nextDigit = 0; nextDigit < 10; nextDigit++) {
next[nextDigit] = BigInteger.ZERO;
for (int digit = 0; digit < 10; digit++) {
if (Math.abs (digit - nextDigit) >= 4) {
next[nextDigit] = next[nextDigit].add (counts[digit]);
}
}
}
temp = counts;
counts = next;
next = temp;
}
BigInteger sum = BigInteger.ZERO;
for (BigInteger i : counts) sum = sum.add (i);
return sum;
}
}
用于下一代(在示例中n = 3)。当算法完成计算counts
时,它会交换两个数组,这意味着我们将使用此代的next
作为下一代的当前数据。
它有3个for循环。外循环简单地计算几代,并且根本不使用。 next
计算下一代中的数字,而next
计算当前一代中的数字。当他们至少相隔4分钟时,我们会进行补充。
如果你想知道,n = 1000的结果确实很大,我花了165毫秒来计算:
58671138329570171371420484902268532315073277852051653969830525802838628724212731137694290047005040297045274423072752812252866695216074181116219893270512906481125049825987756071510466880415373048496191391932743103313044071304405218219902707133109687674960299002863298632965964118240544824530569540542700793488917467060307664191744432111922492168260259079355618958225678548171234101375097873342091776899282686824362584042717489292059166512255400959907373002265039739675037774831081921743873154470907306563401667845616259033848968890244196752759640923743592116170624821165172596009768024780906078208584276112384909371479169927564723938874400811048288种可能性。
答案 1 :(得分:0)
也许您可以使用两个for循环来重写for标头以缩短它:
int previous = out[pos_to_fill-1];
int i;
//lower than 4
for (i=0;i<previous-4;i++) {
//... for cycle body
}
//higher than 4
for (i=previous+4;i<=9;i++) {
//... for cycle body (the same)
}
为了不重复循环体,如果它没有很多参数,我会把身体放到功能
注意:未经测试,启动i
值和条件可能不好,这只是一个想法