【问题标题】:Need help coming up with a Cake sorting algorithm in Java需要帮助提出 Java 中的蛋糕排序算法
【发布时间】:2025-11-21 14:15:02
【问题描述】:

好的,这就是我必须做的事情

作为 MCI (Mammoth Cakes Incorporated) 的员工,您的工作是创造超大型 分层生日蛋糕。分层生日蛋糕是通过将小圆形蛋糕层和 将它们堆叠在一起。

为了完成你的工作,你站在一条大传送带前 而不同大小的层从你面前经过。当你看到你喜欢的,你可以把它拿下来 传送带并将其添加到您的蛋糕中。

您可以根据需要在蛋糕上添加任意多的层, 只要你遵守这些规则:

一旦在蛋糕上添加了一层,就无法移动。 (它弄乱了结冰。)因此,层 只能添加到蛋糕顶部。

每一层只在你面前经过一次。你可以接受也可以离开。如果你拿了,你 必须把它加到你的蛋糕上。如果你离开它,它会沿着传送带移动, 永远不会回来。

蛋糕中的每一层都必须至少与下面的层一样小。你不能放置一个 较大的层在较小的层之上。

您将被提前告知从传送带上下来的层的直径(以英寸为单位)。 你的工作是使用这些层创建最高的蛋糕。 例如,假设下面的列表代表向下的层的直径 传送带:8 16 12 6 6 10 5

假设您将第一层(直径为 8 英寸)用于您的蛋糕。这意味着您可能不会采用第二层(因为您已经有一个 8 英寸大小的层,并且 16 英寸 > 8 英寸)。同样,你不能 取第三层,但你可以取第四层(因为 6”

之后,您可以 也取第五层(规则就是最上面的层不能再大;可以一样 尺寸)。以这种方式进行,我们可以创建一个高度为 4 层的蛋糕:8 6 6 5 但是,如果我们让第一层继续,从第二层开始,我们可以创建一个 高度为5的蛋糕:16 12 6 6 5

您的程序将处理多个输入集,每行一个。每行都以整数 N 开头, 后跟 N 个正整数,表示蛋糕层的大小,按它们的顺序排列 到达传送带。 N 永远是一个非负整数,0 N 100,000。每一层 直径在 1 到 100,000 之间,包括 1 到 100,000。 N = 0 的行表示结束 输入

 Sample Input
7 8 16 12 6 6 10 5
10 45 25 40 38 20 10 32 25 18 30
10 10 9 8 7 6 5 4 3 2 1
0

Sample Output
5
6
10

问题:找到最高层的蛋糕

这是我目前所写的:

import java.io.*;
import java.util.*;

public class cake
{
    private static String line;
    private static ArrayList storage = new ArrayList();
    private static Integer highestStack = 0;

    public static void main(String [] args)throws IOException
    {
          FileReader fin = new FileReader("cake.in");
        BufferedReader infile = new BufferedReader(fin);

        FileWriter fout = new FileWriter("cake.out");
        BufferedWriter outfile = new BufferedWriter(fout);


        line = infile.readLine();
        do
        {

            String[] temp = line.split(" ");
            String number;


                for(int j = temp.length-1; j!=0;  j--)
                {

                   if(Integer.parseInt(temp[j]) <= Integer.parseInt(temp[j-1]))
                   {
                       highestStack++;
                   }

                }

               storage.add(highestStack);
            //  Collections.sort(storage);



            line = infile.readLine();
        }while(!line.equals("0"));


        infile.close();
        outfile.close();

    }

}

【问题讨论】:

  • 看起来您正在解决问题(尽管我看不到您在哪里输出答案)。你有什么问题?
  • 我没有解决问题,这只是我写的。问题是我如何想出最高的一叠蛋糕。
  • @Steffan Harris:这门课是关于了解什么是动态编程吗?如果是这样,请忽略所有建议 Java 解决方案的答案,他们错过了 dynamic programming 标记。
  • @Steffan Harris:我要补充一点,我可以编写一个测试用例,使这里建议的所有非动态编程解决方案都惨遭失败,而 DP 解决方案会嘲笑这个问题;)
  • @Webinator:嗯,它确实是一个算法类,动态编程只是我们所做的主题之一。此外,我们可以选择用 Java 或 C++ 编写程序。我选择 Java 是因为我喜欢它的内置方法,尤其是在处理字符串时。我现在真正关于 DP 的只是你将可能的组合存储在某种数据结构中。

标签: java dynamic-programming


【解决方案1】:

正如我对几个答案的评论完全没有抓住重点,这是一个动态编程问题。

现在您添加了约束,很明显运行在 O(n^2) 中的动态编程解决方案是可行的方法,而且 N 不会超过 100 000 使得使用 DP 求解变得容易(使用非 DP 算法可能很难求解)。

每时每刻,你都必须问自己“在'x'之前我能做的最好的事情是什么”

这是第一个示例的样子:

 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 (Best we can do using pieces: 5 )
 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2 (Best we can do using pieces: 5 10 )
 0 0 0 0 0 1 2 2 2 2 2 2 2 2 2 2 2 (Best we can do using pieces: 5 10 6 )
 0 0 0 0 0 1 3 3 3 3 3 3 3 3 3 3 3 (Best we can do using pieces: 5 10 6 6 )
 0 0 0 0 0 1 3 3 3 3 3 3 4 4 4 4 4 (Best we can do using pieces: 5 10 6 6 12 )
 0 0 0 0 0 1 3 3 3 3 3 3 4 4 4 4 5 (Best we can do using pieces: 5 10 6 6 12 16 )
 0 0 0 0 0 1 3 3 4 4 4 4 4 4 4 4[5] (Best we can do using pieces: 5 10 6 6 12 16 8 )

 Tallest cake as a height of: 5

阅读上面一行的方法很简单。我们以第一行为例:

0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

这意味着我们可以制作的最高蛋糕的底数从 5 到 16 不等,它是由一个元素(我们的第一块,“5”)制成的。

然后我们得到'10',我们得到线:

0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2

这意味着我们可以从 5 到 9 制作的最高蛋糕将有一个元素(我们的“5”),而从 10 到 16 我们可以填充两块(5 和 10)。

然后你可以这样重复,如果你愿意的话,最多可以有 100 000 个元素。

在我的计算机上,使用动态编程解决完整的 100 000 个解决方案只需不到 20 秒。

这是解决您的问题并输出上述内容的代码。我故意添加了输出语句,以便您可以看到正在发生的事情(它只会在相对较小的数字下看起来很漂亮,也就是说,它实际上只是为了了解算法正在发生的事情)。

public static void main( String[] args ) {
    doIt( new int[] {8,16,12,6,6,10,5} );
    doIt( new int[] {0, 45, 25, 40, 38, 20, 10, 32, 25, 18, 30} ); 
    doIt( new int[] {10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} );
}

public static void doIt( int[] r ) {
    final int[] a= new int[r.length];
    int max = Integer.MIN_VALUE;
    for (int i = 0; i < a.length; i++) {
        max = Math.max( max, a[i] );
        a[(a.length-1)-i] = r[i];
    }
    final int[] s = new int[max+1];
    for (int i = 0; i < a.length; i++) {
        final int size = a[i];
        s[size]++;
        for (int j = size+1; j < s.length; j++) {
            s[j] = Math.max( s[j-1], s[j] );
        }
        for (int j = 0; j < s.length; j++) {
            System.out.print( " " + ((s[j]) > 9 ? "" : " ") + s[j] );
        }
        System.out.print( " (Best we can do using pieces: " );
        for (int k = 0; k <= i; k++) {
            System.out.print( a[k] + " " );
        }
        System.out.println( ")" );
    }
    System.out.println( "Tallest cake as a height of: " + s[s.length-1] );
}

【讨论】:

    【解决方案2】:

    我不确定你到底在问什么。所以,我会给你一些一般性的提示。

    查看 Stack 数据结构,而不是 ArrayList。 一层到堆栈上,然后使用 peek 对照传送带中的当前物品检查蛋糕堆栈最顶层的直径。

    如果目标是找到可能最高的蛋糕,一种天真的方法是简单地应用上述算法,从传送带的第一层开始,一直到最后,并记录最终高度(stack .size())。然后重复以传送带中的第二个项目为基础,然后是第三个项目,依此类推,将得到的高度与每个循环结束时记录的最大值进行比较。

    【讨论】:

      【解决方案3】:

      让我们来看看这个过程。每次我们在流水线上遇到一个层,我们都会做出一个决定:使用还是不使用这个层?总体而言,最好的结果是以下两种结果中较好的结果:

      • 我们使用这一层,并在其上使用剩余的层不大于该层构建最高的蛋糕。

      • 我们不使用这一层,而是使用剩余的任何层构建最高的蛋糕。

      我们可以简单地用递归来建模——伪代码:

      tallest(remaining_layers, base_size) = # set base_size = infinity the first time
          max(
              first_layer + tallest(other_layers, size(first_layer)),
              tallest(other_layers, base_size)
          )
          where first_layer = first(remaining_layers),
                other_layers = rest(remaining_layers)
      

      但是,这不会自行解决,因为我们应该使用动态编程。

      这个想法是我们递归调用tallestother_layers 两次。如果我们可以调用它一次,并且拥有我们需要的所有信息,那不是很好吗?

      我们需要什么信息?好吧,如果我们在 任何 基本尺寸的情况下使用剩余层来制作最高的蛋糕,我们将被设置:我们只需选择可以适合当前层的最高蛋糕,然后看看这是否会产生与整体最高的蛋糕相比有所改善。但诀窍是:即使它没有改进,我们仍然可以获得信息。我们的想法是为每种尺寸列出最“有效”(最小基数)的蛋糕。

      因此,我们的流程如下:

      Set up a list of cakes, with one cake in it that has zero layers.
      # There will be, at all times, one cake in the list of any given height.
      # Starting at zero instead of one makes the iteration neater.
      For each layer on the conveyor belt, working **backwards** from the last:
          Find the tallest cake in the list that fits on this layer.
          Construct the cake 'c' consisting of that cake on top of this layer.
          If there is already a cake in the list of the same height as 'c':
              If the other cake has a smaller base, throw 'c' away. # It didn't help.
              Otherwise, remove the other cake from the list. # 'c' is better.
          If we still have 'c', add it to the list.
      The tallest possible cake for the input is now the tallest one in the list.
      

      【讨论】:

        【解决方案4】:

        这个输入序列很棘手:

        10 45 25 40 38 20 10 32 25 18 30
        

        只跳过引入层的简单方法会找到这些 [cakes]:

        [10]  45   25   40   38   20   10   32   25   18   30 
         10  [45   25]  40   38   20   10   32   25   18   30
         10   45  [25]  40   38   20   10   32   25   18   30
         10   45   25  [40   38   20   10]  32   25   18   30   <-- naive tallest, 4
         10   45   25   40  [38   20   10]  32   25   18   30
         10   45   25   40   38  [20   10]  32   25   18   30
         10   45   25   40   38   20  [10]  32   25   18   30
         10   45   25   40   38   20   10  [32   25   18]  30
         10   45   25   40   38   20   10   32  [25   18]  30
         10   45   25   40   38   20   10   32   25  [18]  30
         10   45   25   40   38   20   10   32   25   18  [30]
        

        游戏规则允许您跳过任何层,而不仅仅是领先层,因此在这种情况下,正确的最高蛋糕应该是:

         10  [45]  25  [40] [38]  20   10  [32] [25] [18]  30 
        

        或者只写出选定的图层:

         45   40   38   32   25   18  
        

        【讨论】:

        • 但这根本不是答案,不是吗?此外,Dynamic Programming 算法不是“幼稚”或“不幼稚”的,它们是 DP,不关心这些细节。
        【解决方案5】:

        您要解决的问题是一个动态编程问题(尽管很简单)。

        算法

        public static int findMaxHeight(int[] layers) {
          int[] max = new int[layers.length];
          
          for(int i=layers.length - 1; i >= 0; i--) {
            int localMax = 0;
            for(int j=0; j < layers.length; j++) {
              if(layers[j] < layers[i]) {
                if(max[j] > localMax) {
                  localMax = max[j];
                }
              }
            }
        
            max[i] = localMax + 1;    
          }
          
          int height = 0;
        
          for(int i=0; i < max.length; i++) {
            if(max[i] > height) {
              height = max[i];
            }
          }
        
          return height;
        }
        

        一步一步

        作为其工作原理的一个步骤,请考虑:

        8 16 12 6 6 10 5
        

        由于我们的顺序是相反的,

        5 10 6 6 12 16 8
        

        从 5 开始,[] 中有小于 5 个值:

        5 10 6 6 12 16 8
        1
        

        从 [5],max[5] = 1 所以 1+1

        5 10 6 6 12 16 8
        1  2
        

        等等……

        5 10 6 6 12 16 8
        1  2 2 3  4  5 4
        

        然后我们找出列表 [1, 2, 2, 3, 4, 5, 4] 的最大值,即 5。

        而且,如上所述,这是对他在问题描述中逐步介绍的示例的正确答案。


        工作原理

        该算法通过保存每一层的最大值来工作。该问题解释说,对于任何给定的层,它只能堆叠小于或等于其直径的蛋糕。因此,任何给定层的最大值将始终是带上相同或更小的层的最大值加 1(计算层本身)。如果没有可以堆叠的层,我们知道该层的最大值为 1。

        【讨论】:

        • 您的想法完全正确,但最好解释一下它是如何工作的。在这个和我的回答之间(它试图在更高层次上解释事情,同时跳过实现细节)应该满足 OP。 :)
        • 感谢您的发帖。在这个解决方案中它们不是错误的问题,因为皮带上的第一个数字不是它的一部分;它只是告诉有多少蛋糕从传送带上下来
        • @Steffan 哎呀,对不起。我在答案中修复了它。
        【解决方案6】:

        其实很简单,是这样的:

        int[] layers = new int[] {x1,x2,x3...xn};    
        int[] count = new int[layers.length];
        
        for(int i = 1; i < layers.length; i++)
        {             
               for(int j = i+1 ; j < layers.length; j++)
               {
                 if ( layers[j] >= layers[i]) count[i]++; 
               }     
         }
        
         answer = Collections.max(Arrays.asList(count));
        

        【讨论】:

          最近更新 更多