【问题标题】:Any other solution for recursive solution递归解决方案的任何其他解决方案
【发布时间】:2015-10-25 06:17:02
【问题描述】:

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

【问题讨论】:

  • 我可能解释错了,但不是 39518 (+6)(-4)(-4)(+7) 吗?
  • 每个数字之间的差异应该是至少 4(绝对值),如果我猜对了@DennisCallanan
  • [...] 即使输入大小低至 1000。 这意味着您正在尝试处理一千位数的数字,对吗?这不是十个trecendotrigintillion 可能的值吗?
  • @maskacovnik 这是 10^1000。
  • @RobbyCornelissen :没有那么多价值。每个新数字平均允许 4.2 种可能性。 4^1000 只有 ~ 10^600。但恐怕还是远远超出了单个 int 变量甚至 long long int 的容量! (大调是 5^1000 ~ 10^700)

标签: c algorithm recursion dynamic-programming


【解决方案1】:

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

更长的答案: 基本上,您正在迭代所有可能的解决方案。增加计数的唯一语句是count++,因为我们必须达到一个超过 600 位的数字,这需要一段时间。 (即使不会对每个 count++ 进行函数调用)

所以我们需要以某种方式增加计数,而不是当时只有 1 个。怎么做?

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

但仍然有一个模式。 30 为下一代生成 6 个新数字这一事实意味着 405060 和所有其他以 0 结尾的两位数字也将生成 6 个新数字。 15 产生 3 个新号码,所有其他以 5 结尾的号码也会产生。

如果我们从计算 n=2 开始,而不是记住所有 36 个数字,我们会记住这个数组:[6, 5, 4, 3, 2, 2, 2, 3, 4, 5]。这个数组意味着我们并不确切知道这 36 个数字是什么,但其中 6 个以 0 结尾,其中 5 个以 1 结尾,4 个以 2 结尾,依此类推。

现在我们可以通过做一些加法来计算 n=3 的相同数组。 0 可以从 456789 生成。将它们全部加起来意味着对于 n=3,将有 2 + 2 + 2 + 3 + 4 + 5 = 18 个以 0 结尾的数字。 n=3 的整个数组是[18, 16, 14, 12, 15, 16, 15, 18, 20, 22]

不幸的是,我不会说 c,但这是 java 中的解决方案。

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;                                                         
    }                                                                       

}

它有两个数组:counts 用于当前一代的数组(在上面的示例中 n=2)和next 用于下一代(在示例中 n=3)。当算法完成计算 next 时,它会交换两个数组,这意味着我们将使用这一代的 next 作为下一代的电流。

它有 3 个 for 循环。外循环只计算代数,根本不使用。 nextDigit 计算下一代的数字,而digit 计算当前一代的数字。当它们至少相距 4 时,我们进行加法。

如果你想知道,n=1000 的结果确实很大,我花了 165 毫秒来计算:

58671138329570171371420484902268532315073277852051653969830525802838628724212731137694290047005040297045274423072752812252866695216074181116219893270512906481125049825987756071510466880415373048496191391932743103313044071304405218219902707133109687674960299002863298632965964118240544824530569540542700793488917467060307664191744432111922492168260259079355618958225678548171234101375097873342091776899282686824362584042717489292059166512255400959907373002265039739675037774831081921743873154470907306563401667845616259033848968890244196752759640923743592116170624821165172596009768024780906078208584276112384909371479169927564723938874400811048288种可能性。 P>

【讨论】:

  • 我真的看不出你在哪里重用 n=2 来计算 n=3?我认为您的逻辑可能是错误的,因为我不太了解Java。让我们计算n = 3。因此,您所做的只是蛮力。 15, 16, 17, 18, 19, 26, 27, 28, 29.... 等等。有了这个,你再添加一个元素 159, 160, 170, 180, .... 一个 'for' 循环用于从 1 到 9 的第一个数字。第二个 'for' 循环用于第二个数字和第三个'for'循环检查所有可能性。即使我已将其标记为正确,您可以在代码周围添加一些 cmets 吗?
  • 好的,我编辑了文本并添加了一些解释。另外,我应该感谢您让我再次查看它,因为我的代码中有一个愚蠢的错误(需要交换数组,不能只分配)
【解决方案2】:

也许您可以使用两个 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 的值和条件可能不好,这只是一个想法

【讨论】:

    猜你喜欢
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    • 2018-12-30
    • 2018-11-30
    相关资源
    最近更新 更多