需要帮助提出Java中的Cake排序算法

时间:2010-12-12 19:11:23

标签: java dynamic-programming

好的,这就是我要做的事情

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

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

您可以根据需要为蛋糕添加任意数量的图层, 只要你遵守这些规则:

将一个图层添加到蛋糕后,就无法移动。 (它弄乱了结冰。)因此,层次 只能添加到你的蛋糕顶部。

每一层只在你面前经过一次。你可以接受或离开它。如果你接受它,你 必须将它添加到你的蛋糕顶部。如果你离开它,它会沿传送带向下移动, 永远不会回来。

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

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

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

之后,你可以 也采取第五层(规则只是顶部的层不能更大;它可以是相同的 尺寸)。以这种方式进行,我们可以制作一个高度为4层的蛋糕:8 6 6 5 但是,如果我们让第一层继续并从第二层开始,我们可以创建一个 蛋糕的高度为5:16 12 6 6 5

您的程序将处理多个输入集,每行一个。每一行都以整数N开头, 接着是N个正整数,按照它们的顺序表示蛋糕层的大小 到达传送带。 N将始终为非负整数,0 N 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();

    }

}

6 个答案:

答案 0 :(得分:2)

正如我评论几个完全忽略了这一点的答案,这是一个动态规划问题。

现在您已经添加了约束,很明显,在 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

这意味着我们可以制作的最高蛋糕,其基数为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)。

如果你愿意,你可以这样重复,最多可以有10万个元素。

在我的计算机上,完整的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] );
}

答案 1 :(得分:1)

我不确定你在问什么,确切地说......所以,我会给你一些一般性的提示。

查看Stack数据结构,而不是ArrayList。 将一个图层推入堆栈,然后使用 peek 检查蛋糕堆最顶层的直径与传送带中的当前项目的对比。

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

答案 2 :(得分:1)

这个输入序列很棘手:

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

答案 3 :(得分:1)

您尝试解决的问题是动态编程问题(尽管很简单)。

算法

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。

答案 4 :(得分:1)

让我们来看看这个过程。每次我们在装配线上遇到一个图层时,我们都会做出决定:是否使用此图层?总体上最好的结果是以下两个结果中的更好结果:

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

  • 我们不使用此图层,并使用任何其余图层构建最高的蛋糕。

我们可以使用递归 - 伪代码

简单地对此进行建模
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)

然而,由于我们应该使用动态编程,因此不会自行削减它。

我们的想法是,我们两次以tallest递归调用other_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.

答案 5 :(得分:0)

实际上很简单,就是这样:

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