任何其他递归解决方案

时间:2015-08-03 08:01:06

标签: c algorithm recursion dynamic-programming

我试图找到有多少长度为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;
}

2 个答案:

答案 0 :(得分:4)

简短回答:不要使用递归,自下而上使用动态编程。

更长的回答: 基本上,您将迭代所有可能的解决方案。增加计数的唯一声明是count++,因为我们必须使用超过600位的数字,这需要花费一些时间。 (即使它不会为每个count++

进行函数调用

所以不知怎的,我们需要增加数量,然后只增加1。怎么做?

假设我们已经知道n = 2的答案是36种可能性。这有助于我们计算n = 3的可能性吗?不,不是真的,因为我们不知道这36个数字是什么。其中一个两位数字是15,可以扩展为150151159(3种可能性)。另一个两位数字是30,可以扩展为304305306307308和{{1} (6种可能性)。我们显然不能将36乘以一些常数因子来得到n = 3的解。

但是仍有一种模式。 309为下一代产生6个新数字的事实意味着304050以及以{{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的相同数组。可以120456生成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值和条件可能不好,这只是一个想法