Algorithm 给定一个整数N和一组运算,以最少的步骤将N减为1

Algorithm 给定一个整数N和一组运算,以最少的步骤将N减为1,algorithm,Algorithm,对于给定的整数N,我们可以使用以下操作: 如果N可以除以3:除以3 如果N可以除以2:除以2 减去1 我如何找到一种策略,以最少的步骤达到1?将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止 从根到1的路径就是解决方案。将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止 从根到1的路径就是解决方案。有一种快速动态编程解决方案:- minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(

对于给定的整数N,我们可以使用以下操作:

  • 如果N可以除以3:除以3
  • 如果N可以除以2:除以2
  • 减去1

  • 我如何找到一种策略,以最少的步骤达到1?

    将其视为宽度优先的树遍历

    根节点是N。操作是指向其子节点的边

    到达1时停止


    从根到1的路径就是解决方案。

    将其视为宽度优先的树遍历

    根节点是N。操作是指向其子节点的边

    到达1时停止


    从根到1的路径就是解决方案。

    有一种快速动态编程解决方案:-

    minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
    
    public static int decompose(int n) {
            int steps [] = new int[n+1];
            steps[1] = 0;
            for(int i=2;i<=n;i++) {
                int min = n;
                if(i%2==0) {
                    min = Math.min(min,steps[i/2]);
                }
                if(i%3==0) {
                    min = Math.min(min,steps[i/3]);
                }
                min = Math.min(min,steps[i-1]);
                steps[i] = min + 1;
            }
            int k =n;
            System.out.println("Steps:");
            while(k>1) {
                if(k%3==0&&steps[k/3]+1==steps[k]) {
                    System.out.println("div 3");
                    k=k/3;
                }
                else if(n%2==0&&steps[k/2]+1==steps[k]) {
                    System.out.println("div 2");
                    k=k/2;
                }
                else {
                    System.out.println("minus 1");
                    k=k-1;
                }
            }
    
            return(steps[n]);
    
        }
    
    注意:如果N不能被3或2整除,则不要将其包含在DP方程中

    时间复杂性:
    O(N)
    空间复杂性:
    O(N)

    DP解决方案的Java代码:-

    minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
    
    public static int decompose(int n) {
            int steps [] = new int[n+1];
            steps[1] = 0;
            for(int i=2;i<=n;i++) {
                int min = n;
                if(i%2==0) {
                    min = Math.min(min,steps[i/2]);
                }
                if(i%3==0) {
                    min = Math.min(min,steps[i/3]);
                }
                min = Math.min(min,steps[i-1]);
                steps[i] = min + 1;
            }
            int k =n;
            System.out.println("Steps:");
            while(k>1) {
                if(k%3==0&&steps[k/3]+1==steps[k]) {
                    System.out.println("div 3");
                    k=k/3;
                }
                else if(n%2==0&&steps[k/2]+1==steps[k]) {
                    System.out.println("div 2");
                    k=k/2;
                }
                else {
                    System.out.println("minus 1");
                    k=k-1;
                }
            }
    
            return(steps[n]);
    
        }
    
    公共静态int分解(int n){
    整数步[]=新整数[n+1];
    步骤[1]=0;
    对于(int i=2;i1){
    如果(k%3==0&&steps[k/3]+1==steps[k]){
    系统输出打印LN(“第3部分”);
    k=k/3;
    }
    else如果(n%2==0&&steps[k/2]+1==steps[k]){
    系统输出打印LN(“第2部分”);
    k=k/2;
    }
    否则{
    系统输出打印项次(“减1”);
    k=k-1;
    }
    }
    返回(步骤[n]);
    }
    
    有一种快速动态编程解决方案:-

    minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
    
    public static int decompose(int n) {
            int steps [] = new int[n+1];
            steps[1] = 0;
            for(int i=2;i<=n;i++) {
                int min = n;
                if(i%2==0) {
                    min = Math.min(min,steps[i/2]);
                }
                if(i%3==0) {
                    min = Math.min(min,steps[i/3]);
                }
                min = Math.min(min,steps[i-1]);
                steps[i] = min + 1;
            }
            int k =n;
            System.out.println("Steps:");
            while(k>1) {
                if(k%3==0&&steps[k/3]+1==steps[k]) {
                    System.out.println("div 3");
                    k=k/3;
                }
                else if(n%2==0&&steps[k/2]+1==steps[k]) {
                    System.out.println("div 2");
                    k=k/2;
                }
                else {
                    System.out.println("minus 1");
                    k=k-1;
                }
            }
    
            return(steps[n]);
    
        }
    
    注意:如果N不能被3或2整除,则不要将其包含在DP方程中

    时间复杂性:
    O(N)
    空间复杂性:
    O(N)

    DP解决方案的Java代码:-

    minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
    
    public static int decompose(int n) {
            int steps [] = new int[n+1];
            steps[1] = 0;
            for(int i=2;i<=n;i++) {
                int min = n;
                if(i%2==0) {
                    min = Math.min(min,steps[i/2]);
                }
                if(i%3==0) {
                    min = Math.min(min,steps[i/3]);
                }
                min = Math.min(min,steps[i-1]);
                steps[i] = min + 1;
            }
            int k =n;
            System.out.println("Steps:");
            while(k>1) {
                if(k%3==0&&steps[k/3]+1==steps[k]) {
                    System.out.println("div 3");
                    k=k/3;
                }
                else if(n%2==0&&steps[k/2]+1==steps[k]) {
                    System.out.println("div 2");
                    k=k/2;
                }
                else {
                    System.out.println("minus 1");
                    k=k-1;
                }
            }
    
            return(steps[n]);
    
        }
    
    公共静态int分解(int n){
    整数步[]=新整数[n+1];
    步骤[1]=0;
    对于(int i=2;i1){
    如果(k%3==0&&steps[k/3]+1==steps[k]){
    系统输出打印LN(“第3部分”);
    k=k/3;
    }
    else如果(n%2==0&&steps[k/2]+1==steps[k]){
    系统输出打印LN(“第2部分”);
    k=k/2;
    }
    否则{
    系统输出打印项次(“减1”);
    k=k-1;
    }
    }
    返回(步骤[n]);
    }
    
    正如姆贝基什所提到的,您可以将其视为BFS遍历,它比自底向上的DP方法具有更好的时间和空间复杂性。您还可以在遍历中应用分支和绑定(B&B)启发式排序,以便在我们之前已经看到标记值的节点上修剪树的分支。与实际的B&B启发式不同,这不会删减最佳解决方案,因为它不涉及对最佳解决方案可能位于何处的任何有根据的猜测。我将给出一个直观的示例,并将算法减少到0以更好地说明

    下面是一个完整的操作树,将10减少到0:

       --------10---------
       5         -----9----
    ---4---     -3-      ------8------  
    2    -3-    1 2    --4--         7        
    1    1 2    0 1    2  -3-   -----6------   
    0    0 1      0    1  1 2   2   -3-    5   
           0           0  0 1   1   1 2  --4-- 
                            0   0   0 1  2  -3-
                                      0  1  1 2
                                         0  0 1
                                              0 
    
    因为我们在做BFS,我们实际上会在第一个零处停止,如下所示,而不是构建树的更深部分:

       --------10------
       5         -----9--------
    ---4---     -3-     ------8------  
    2    -3-    1 2     4           7        
    1    1 2    0 
    
    然而,我们可以通过B&B启发式进一步减少分支的数量,使其看起来像这样(这对大量的分支产生了巨大的影响):

    时间复杂度:
    O(logn)
    空间复杂度:
    O(logn)
    (我想)

    下面是Python3代码,输入为1 googol(10^100),在我的计算机上运行大约需要8秒,内存大约为350MB。您也可以在以下位置在线运行它:


    正如mbeckish提到的,您可以将其视为BFS遍历,它比自底向上的DP方法具有更好的时间和空间复杂性。您还可以在遍历中应用分支和绑定(B&B)启发式排序,以便在我们之前已经看到标记值的节点上修剪树的分支。与实际的B&B启发式不同,这不会删减最佳解决方案,因为它不涉及对最佳解决方案可能位于何处的任何有根据的猜测。我将给出一个直观的示例,并将算法减少到0以更好地说明

    下面是一个完整的操作树,将10减少到0:

       --------10---------
       5         -----9----
    ---4---     -3-      ------8------  
    2    -3-    1 2    --4--         7        
    1    1 2    0 1    2  -3-   -----6------   
    0    0 1      0    1  1 2   2   -3-    5   
           0           0  0 1   1   1 2  --4-- 
                            0   0   0 1  2  -3-
                                      0  1  1 2
                                         0  0 1
                                              0 
    
    因为我们在做BFS,我们实际上会在第一个零处停止,如下所示,而不是构建树的更深部分:

       --------10------
       5         -----9--------
    ---4---     -3-     ------8------  
    2    -3-    1 2     4           7        
    1    1 2    0 
    
    然而,我们可以通过B&B启发式进一步减少分支的数量,使其看起来像这样(这对大量的分支产生了巨大的影响):

    时间复杂度:
    O(logn)
    空间复杂度:
    O(logn)
    (我想)

    下面是Python3代码,输入为1 googol(10^100),在我的计算机上运行大约需要8秒,内存大约为350MB。您也可以在以下位置在线运行它:


    你是说正确的答案是12,因为((12/3)/2)-1=1?是的。在10中,((10-1)/3)3)是正确的解,而不是((10/2)-1)/2))/2。@Nabla只要一个就足够了。在10中,答案是3。这让我想起了一点,其中
    3n+1
    是这里的两个步骤的倒数,
    /2
    涵盖了第三个步骤。我不知道这是否意味着有比暴力更好的解决办法。也许值得在一个数学SE网站上讨论。@HyeonWooKim如果N不能被3,2整除怎么办?你是说正确的答案是12,因为((12/3)/2)-1=1?是的。在10中,((10-1)/3)3)是正确的解,而不是((10/2)-1)/2))/2。@Nabla只要一个就足够了。在10中,答案是3。这让我想起了一点,其中
    3n+1
    是这里的两个步骤的倒数,
    /2
    涵盖了第三个步骤。我不知道这是否意味着有比暴力更好的解决办法。也许值得在一个数学SE网站上发表一个话题。@HyeonWooKim如果N不能被3,2整除,那么d是什么呢