Convert a double list to a grouped string - java

The program has an input a list of doubles and the output needs to be a string containing the list values grouped by their value. The list values will be grouped if they are equal.
Something like:
input 9,77,5,5,31 => output 9 77 2*5 31
I created an algorithm in C# (in Java I think that is almost the same) for this but I am not sure if it can be improved regarding its speed or code quaility, or if it has some bugs that I could not see. The algorithm having also some more input, output examples is below.
List<double> input = new List<double> { 11, 32, 32, 43}; // output 11 2*32 43
//List<double> input = new List<double> { 11, 11, 43, 43 }; // output 2*11 2*43
//List<double> input = new List<double> { 10, 11, 12, 13, 14, 15, 16 }; // output 10 11 12 13 14 15 16
//List<double> input = new List<double> { 11, 11, 11, 11, 11 }; // output 5 * 11
//List<double> input = new List<double> { 11, 11, 32, 22, 22, 22, 4, 10, 10 }; // output 2*11 32 3*22 4 2*10
string listAsString = string.Empty;
double nextElem = double.MinValue;
for (int i = 0; i < input.Count; i++)
{
double currentElem = input[i];
if (i + 1 < input.Count)
{
nextElem = input[i + 1];
}
int equalCount = 0;
while (currentElem.Equals(nextElem) && i < input.Count)
{
equalCount++;
i++;
currentElem = nextElem;
if (i < input.Count)
{
nextElem = input[i];
}
}
if (equalCount < 2)
{
listAsString += currentElem + " ";
}
else
{
listAsString += equalCount + "*" + currentElem + " ";
i--;
}
}
Console.WriteLine(listAsString);
Please let me know if you noticed some bugs or see some improvements that can be done.
Also if you know another implementation of this requirement please add it so that a comparation regarding results, speed, code quality between the algorithms can be done... and find the best way to handle this.

Since the requirement is to group only consecutive equal values, the Dictionary and LINQ GroupBy approaches mentioned in another answer do not apply because they will produce incorrect result for input sequence like 1,2,1. Also there is no standard LINQ method for doing such grouping (except eventually Aggregate method, but it's no more than inefficient for / foreach loop equivalent).
Shortly, your algorithm is the best for such task. But implementation is not.
The main bottleneck is the string concatenation as mentioned by Peroxy, which (also mentioned in the other answer) is easily fixable by utilizing the StringBuilder class. Once you do that, the performance will be just fine.
The other issue I see in the implementation is usage of special values (double.MinValue), duplicate corner case checks, decrementing for loop variable inside the body etc. So although it probably works and I don't see directly a bug, it's kind of hard to follow the algorithm logic and spot a potential bug just reading the implementation. The algorithm itself is quite simple, I would implement it this way:
static string ListAsString(List<double> input)
{
var sb = new StringBuilder();
for (int i = 0; i < input.Count; )
{
var value = input[i];
int count = 1;
while (++i < input.Count && input[i] == value)
count++;
if (sb.Length > 0) sb.Append(' ');
if (count > 1) sb.Append(count).Append('*');
sb.Append(value);
}
return sb.ToString();
}
which IMO is quite easier to follow. Note that there is no duplicate code, no special values and the loop variable i advancing is done only in one place inside the outer loop body. Again, this has nothing to do with performance (which is provided by the StringBuilder usage), but simply readability, redundancy elimination and less error prone.

Personally, I see great potential with Dictionary usage here, here is a quick solution I made with a dictionary implementation:
var input = new List<double> { 9, 77, 5, 5, 31 };
var dict = new Dictionary<double, int>();
var listAsString = new StringBuilder();
foreach (var item in input)
{
if (dict.ContainsKey(item))
dict[item]++;
else
dict[item] = 1;
}
foreach (var item in dict)
{
listAsString.Append(item.Value > 1 ? $"{item.Value}*{item.Key} " : $"{item.Key} ");
}
Console.WriteLine(listAsString);
If you ever wanted a non efficient LINQ one liner solution:
string result = string.Join(" ", input.GroupBy(i => i)
.Select(x =>
x.Count() > 1 ?
$"{x.Count()}*{x.Key} " :
$"{x.Key} "));
However, I believe your method is written nicely, albeit a bit less readable than the dictionary one, but the main flaw with your solution is that you are using a string when building the final string, you should definitely be using a StringBuilder, I have introduced the StringBuilder in your method and made comparisons between these three methods:
Dictionary | Your method | GroupBy method
------------------------------------------------
2 ms | 0 ms | 5 ms n=3
0 ms | 0 ms | 0 ms n=6
0 ms | 0 ms | 0 ms n=12
0 ms | 0 ms | 0 ms n=24
0 ms | 0 ms | 0 ms n=48
0 ms | 0 ms | 0 ms n=96
0 ms | 0 ms | 0 ms n=192
0 ms | 0 ms | 0 ms n=384
0 ms | 0 ms | 0 ms n=768
0 ms | 0 ms | 0 ms n=1536
1 ms | 0 ms | 1 ms n=3072
3 ms | 2 ms | 3 ms n=6144
5 ms | 4 ms | 6 ms n=12288
8 ms | 7 ms | 14 ms n=24576
14 ms | 13 ms | 25 ms n=49152
31 ms | 32 ms | 66 ms n=98304
80 ms | 59 ms | 146 ms n=196608
149 ms | 123 ms | 294 ms n=393216
246 ms | 218 ms | 504 ms n=786432
483 ms | 428 ms | 1040 ms n=1572864
999 ms | 873 ms | 2070 ms n=3145728
1995 ms | 1784 ms | 3950 ms n=6291456
Your solution is always the fastest, if you want to go for speed, keep your solution, but change it to use StringBuilder, use listAsString.Append(currentElem + " ") instead of listAsString += currentElem + " ".
GroupBy could be used if you will only operate with collections that have n < 1000, use the Dictionary solution if you would rather settle with readability over speed.

Related

What's the difference between these 2 array definitions?

What us the difference between int[][] a = new int[2][3] and int a[][] = new int[][]{{11,12},{13,14,15}}?
I decompiled the .class file and found in the first case, the JVM will use multianwearray to create the array, while in the second case, it will use anewarray.
I think in the first case, JVM will create a continuous space. Am I right?
first case (int[][] a = new int[2][3])
second case (int[][] a = new int[][]{{11,12},{13,14,15}})
I think in the first case, jvm will create a continuous space. Am i right?
No. A multidimensional array is still an array of arrays. And it must be so, because the code that later accesses the array doesn't know how it was created.
Writing
int[][] a = new int[2][3];
allocates the same amount of heap space and the data on the heap has the same structure as if you would write
int[][] a = new int[2][];
a[0] = new int[3];
a[1] = new int[3];
except that the code for the first variant is embedded in the JVM:
https://github.com/openjdk/jdk17u/blob/master/src/hotspot/share/oops/objArrayKlass.cpp#L178 ff
objArrayOop array = allocate(length, CHECK_NULL);
//...
if (length != 0) {
for (int index = 0; index < length; index++) {
ArrayKlass* ak = ArrayKlass::cast(ld_klass);
oop sub_array = ak->multi_allocate(rank-1, &sizes[1], CHECK_NULL);
h_array->obj_at_put(index, sub_array);
}
}
First Case
Creates an array of 2 int arrays, which are of length 3, which are filled with 0s.
| [ ][0] | [ ][1] | [ ][2]
[0][ ] | 0 | 0 | 0
[1][ ] | 0 | 0 | 0
Second Case
Creates a "jagged" array containing { 11, 13 }, and { 13, 14, 15 }. And which contained values are themselves array of ints.
| [ ][0] | [ ][1] | [ ][2]
[0][ ] | 11 | 12 | error
[1][ ] | 13 | 14 | 15

Side by side tables java

I am new and have a question that is dumbfounding me.
I'm doing a conversion table where it is side by side miles to kilometers, a similar question that was never fully answered in 3 years. now i'm only using print(f)(ln) format, no Jtables, nada, simple println.
I got the table to be side by side using for loops but ran into a problem on the output that is kicking my keester.
The output I get is repeating itself 10 times here:
1 1.609 | 20 32.18
2 1.609 | 21 33.789
3 1.609 | 22 35.397
so I used an if statment and %5 == 0 in my for loop and I got this output:
1 1.609 | 20 32.18
2 1.609 | 25 40.225
3 1.609 | 30 48.269
So now i try a break statement, and i get this:
1 1.609 | 20 32.18
2 3.218 | 20 32.18
3 4.768 | 20 32.18
I run into dead code in my second loop but it cuts off at 10 like it should in the right table but dead code in the left so I use a continue statement and get this:
1 1.609 | 20 32.18
2 1.609 | 25 40.225
3 1.609 | 30 48.269
My final correct result should be:
1 1.609 | 20 32.18
2 3.218 | 25 40.225
3 4.768 | 30 48.269
... so on till 10.
Could anybody give me a clue as to how to fix this, I dont want the answer just a little help. Thank you
Edit: how do i make my code output vertical and not horozantal so it is easier to read?
You don't need a nested loop, one for-loop will do the job
int x = 1.609;
int k = 20;
for (int i=1; i<=10; i++) {
System.out.println(i + " " + i*x + " | " + k + " " + k*x);
k+=5;
}

Generating integers of form 2^n - 1 with for loop

I have an assignment that asks me to write a for loop inside a method that will output this sequence:
1 3 7 15 31 ... 255
I know that the pattern is to multiply the number by two then add one (or just to add the exponents of 2 to each number so 1 + 2 = 3 + 4 = 7 + 8 = 15 etc.) but I don't know how exactly to make a loop that'll output that sequence all the way up to 255.
I would just like an example or explanation to guide me a little bit, I don't want anyone to actually give me the exact code I need. Here's what I've done so far:
public static void methodOne() {
for (int j = 1; j <= 255; j *= 2) {
}
}
I tried to use another for loop within the for loop above but it didn't work well, and I'm not sure if that's the right thing to do. I basically want to take j and have it multiplied by two and then add 1 to get the next number in the sequence.
As you noted, the sequence is to double the previous number and add one. Just have your for loop progress like that, and print the number in each iteration:
for (int j = 1; j <= 255; j = (j * 2) + 1) {
System.out.println(j);
}
As is howework, will leave you something to think:
for x in 2:8 range
result = 2^x -1
this is the most succinct form I could think of that uses only additions ( + ) over 1 single tracking variable:
jot 100 |
mawk '$++NF = _+=_++' CONVFMT='%.f' # mawk-1
gawk '$++NF = _+=_++' # gawk, mawk-2, nawk, etc
1 1
2 3
3 7
4 15
5 31
6 63
7 127
8 255
9 511
10 1023
11 2047
12 4095
13 8191
14 16383
15 32767
16 65535
17 131071
18 262143
. . .
30 1073741823
31 2147483647
32 4294967295
33 8589934591
34 17179869183
35 34359738367
36 68719476735
if you wanna go all the way to 2^1023 - 1 without using a big-int library (tested and confirmed working for mawk-1, mawk-2, gawk 5.2.0, and nawk):
jot 1023 |
mawk '$++NF = (((___+=___++)%((_+=_^=_<_)+_*_*_)^_^_)%_ ||
index(___,"e") < (length(___) < _^_^_--)) \
? ___ : sprintf("%.*s%d", -_+(__ = length(_=\
sprintf("%.f",___))),_,substr(_,__)-!!__)' \
CONVFMT='%.16g'
31 2147483647
279 9713344461128645354597309534117594533212034195260697606259062048
69452142602604249087
527 4393470502483590217588416511412091659052438592091715462012456613
8787476373744998733584381700233309151854696392905477491437580723
1981865204004737810631363657727
775 1987223158144907436990693745232003270728814101909371662257986608
6733452194385624145035243633006674917766242952923277737038996224
5646696242104868771205271185818170236930668787910433956560844600
937633663896795708000114284397288455405567
1023 8988465674311579538646525953945123668089884894711532863671504057
8866337902750481566354238661203768010560056939935696678829394884
4072083112464237153197370621888839467124327426381511098006230470
5972654147604250288441907534117123144073695655527041361858167525
5342293149119973622969239858152417678164812112068607

Performance of variable argument methods in Java

I have this question about the performance of a method in Java with a variable number of parameters.
Say I have the following 2 alternatives:
public static final boolean isIn(int i, int v1, int v2) {
return (v1 == i) || (v2 == i);
}
public static final boolean isIn(int i, int... values) {
for (int v : values) {
if (i == v) {
return true;
}
}
return false;
}
Now the main problem comes when I have versions of the first method that go up to 20, 30 or even 50 parameters. Now that just hurts the eyes. Ok, this is legacy code and I'd like to replace all of it with the only one variable arguments method.
Any idea what the impact on performance would be? Any chance the compiler does some optimization for the second method so that it resembles the first form more or less?
EDIT: Ok, maybe I was not clear enough. I don't have performance problems with the methods with 50 arguments. It's just about readability as Peter Lawrey said.
I was wondering about performance problems if I switch to the new method with variable number of arguments.
In other words: what would be the best way to do it if you care about performance? Methods with 50 arguments or the only one method with variable arguments?
I had the same question, and turned to experimentation.
public class ArgTest {
int summation(int a, int b, int c, int d, int e, int f) {
return a + b + c + d + e + f;
}
int summationVArgs(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}
return sum;
}
final static public int META_ITERATIONS = 200;
final static public int ITERATIONS = 1000000;
static public void main(String[] args) {
final ArgTest at = new ArgTest();
for (int loop = 0; loop < META_ITERATIONS; loop++) {
int sum = 0;
final long fixedStart = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summation(2312, 45569, -9816, 19122, 4991, 901776);
}
final long fixedEnd = System.currentTimeMillis();
final long vargStart = fixedEnd;
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summationVArgs(2312, 45569, -9816, 19122, 4991, 901776);
}
final long vargEnd = System.currentTimeMillis();
System.out.printf("%03d:%d Fixed-Args: %d ms\n", loop+1, ITERATIONS, fixedEnd - fixedStart);
System.out.printf("%03d:%d Vargs-Args: %d ms\n", loop+1, ITERATIONS, vargEnd - vargStart);
}
System.exit(0);
}
}
If you run this code on a modern JVM (here 1.8.0_20), you will see that the variable number of arguments cause overhead in performance and possible in memory consumption as well.
I'll only post the first 25 runs:
001:1000000 Fixed-Args: 16 ms
001:1000000 Vargs-Args: 45 ms
002:1000000 Fixed-Args: 13 ms
002:1000000 Vargs-Args: 32 ms
003:1000000 Fixed-Args: 0 ms
003:1000000 Vargs-Args: 27 ms
004:1000000 Fixed-Args: 0 ms
004:1000000 Vargs-Args: 22 ms
005:1000000 Fixed-Args: 0 ms
005:1000000 Vargs-Args: 38 ms
006:1000000 Fixed-Args: 0 ms
006:1000000 Vargs-Args: 11 ms
007:1000000 Fixed-Args: 0 ms
007:1000000 Vargs-Args: 17 ms
008:1000000 Fixed-Args: 0 ms
008:1000000 Vargs-Args: 40 ms
009:1000000 Fixed-Args: 0 ms
009:1000000 Vargs-Args: 89 ms
010:1000000 Fixed-Args: 0 ms
010:1000000 Vargs-Args: 21 ms
011:1000000 Fixed-Args: 0 ms
011:1000000 Vargs-Args: 16 ms
012:1000000 Fixed-Args: 0 ms
012:1000000 Vargs-Args: 26 ms
013:1000000 Fixed-Args: 0 ms
013:1000000 Vargs-Args: 7 ms
014:1000000 Fixed-Args: 0 ms
014:1000000 Vargs-Args: 7 ms
015:1000000 Fixed-Args: 0 ms
015:1000000 Vargs-Args: 6 ms
016:1000000 Fixed-Args: 0 ms
016:1000000 Vargs-Args: 141 ms
017:1000000 Fixed-Args: 0 ms
017:1000000 Vargs-Args: 139 ms
018:1000000 Fixed-Args: 0 ms
018:1000000 Vargs-Args: 106 ms
019:1000000 Fixed-Args: 0 ms
019:1000000 Vargs-Args: 70 ms
020:1000000 Fixed-Args: 0 ms
020:1000000 Vargs-Args: 6 ms
021:1000000 Fixed-Args: 0 ms
021:1000000 Vargs-Args: 5 ms
022:1000000 Fixed-Args: 0 ms
022:1000000 Vargs-Args: 6 ms
023:1000000 Fixed-Args: 0 ms
023:1000000 Vargs-Args: 12 ms
024:1000000 Fixed-Args: 0 ms
024:1000000 Vargs-Args: 37 ms
025:1000000 Fixed-Args: 0 ms
025:1000000 Vargs-Args: 12 ms
...
Even at the best of times, the Vargs-Args never dropped to 0ms.
The compiler does next to no optimisation. The JVM can optimise code but the two methods won't perform anything like each other. If you have lines of code like isIn(i, 1,2,3,4,5,6,7,8,9 /* plus 40 more */) you have more than performance issues to worry about IMHO. I would worry about readability first.
If you are worried about performance pass the arguments as a int[] which is reused.
BTW The most efficient way to look up a large set of int values is to use a Set like TIntHashSet
to #Canonical Chris
I don't think problem at your test come from variable argument. The function sumationVArgs take more time to complete because of for loop.
I created this function and added to the benchmark
int summationVArgs2(int... args) {
return args[0] + args[1] + args[2] + args[3] + args[4] + args[5];
}
and this is what I see:
028:1000000 Fixed-Args: 0 ms
028:1000000 Vargs-Args: 12 ms
028:1000000 Vargs2-Args2: 0 ms
The for loop in function "summationVArgs" is compiled to more operations than add function. It contains add operation to increase iterator, check operation to check condition and branch operation to loop and exit loop, and all of them execute once for each loop except branch opration to exit loop.
Sorry for my bad English. I hop you can understand my English :)
Come back when you have profiler output that says this is a problem. Until then, it's premature optimization.
It will be the same as if you declared
isIn(int i, int[] values) {
However there will be some some small overhead in packaging the variables up when calling your method
Heard about the two optimisation rules:
Don't optimize
(For experts only!) Don't optimize yet
In other words this is nothing you should care about from the performance point of view.

Need help coming up with a Cake sorting algorithm in Java

OK here is what I have to do
As an employee of MCI (Mammoth Cakes Incorporated), it is your job to create extremely large
layered birthday cakes. A layered birthday cake is made by taking small circular cakes layers and
stacking them on top of each other.
To perform your job, you stand in front of a big conveyor belt
while layers of varying sizes pass in front of you. When you see one you like, you may take it off the
conveyor belt and add it to your cake.
You may add as many layers to your cake as you would like,
as long as you follow these rules:
Once a layer is added to your cake it cannot be moved. (It messes up the icing.) Thus, layers
can only be added to the top of your cake.
Each layer passes in front of you only once. You may take it or leave it. If you take it, you
must add it to the top of your cake. If you leave it, it will move on down the conveyor belt,
never to return.
Each layer in your cake must be at least as small as the layer below. You cannot place a
larger layer on top of a smaller one.
You will be told in advance the diameters (in inches) of the layers coming down the conveyor belt.
Your job is to create the tallest cake possible using those layers.
For example, suppose the following list represents the diameters of the layers coming down the
conveyor belt: 8 16 12 6 6 10 5
Suppose you take the first layer (with a diameter of 8”) for your cake. That means you may not take the second layer (since you already have a layer of size 8”, and 16” > 8”). Similarly, you could not
take the third layer, but you could take the fourth layer (since 6” < 8”).
Following that, you could
also take the fifth layer (the rule is simply that the layer on top cannot be larger; it can be the same
size). Proceeding in this fashion we can create a cake with a height of 4 layers: 8 6 6 5
However, if we had let the first layer go on by and started with the second layer, we could create a
cake with a height of 5: 16 12 6 6 5
Your program will process multiple input sets, one per line. Each line will begin with an integer N,
followed by N positive integers representing the sizes of the cake layers in the order that they will be
arriving on the conveyor belt. N will always be a non-negative integer, 0 N 100,000. Each layer
will have a diameter between 1 and 100,000, inclusive. A line where N = 0 marks the end of the
input
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
Question: Find the tallest layer of Cakes
Here is what I have written so far:
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();
}
}
As I commented on several answers totally missing the point, this is a Dynamic Programming problem.
Now that you added the constraints, it is clear that a Dynamic Programming solution running in O(n^2) is the way to go, and the fact that N won't go above 100 000 makes it easy to solve using DP (and probably very hard to solve using non-DP algos).
At every moment, you have to ask yourself "What is the best I can do up to 'x'".
Here's how it looks like for you first example :
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
The way to read a line above is easy. Let's take the first line for example:
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
It means that the tallest cake we can make that has a base of anywhere from 5 to 16 is made of one element (our first piece, the '5').
Then we get the piece '10', and we get the line:
0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2
It means that the tallest cake we can make from 5 to 9 will have one element (our '5') and that from 10 to 16 we can stuff two pieces (5 and 10).
And you repeat like that, with up to 100 000 elements if you want.
On my computer a full 100 000 solution takes less than 20 seconds to be solved using Dynamic Programming.
Here's the code solving your problems and outputting the above. I added output statements on purpose so that you can see what is going on (it will only look pretty with relatively small numbers that said, it is really just to get what is going on with the algorithm).
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] );
}
I'm not certain what you're asking, exactly.. So, I'll give you some general hints.
Look into the Stack data structure, instead of an ArrayList. Push a layer onto the stack, and then use peek to check the diameter of the topmost layer of your cakestack against the current item in the conveyor.
If the goal is to find the tallest possible cake, a naive approach would be to simply apply the above algorithm by starting with the first layer in the conveyor, continuing to the end, and recording the ultimate height (stack.size()). Then repeat with the second item in the conveyor as your base, and then the third, and so on, comparing the resulting height against the recorded max at the conclusion of each loop.
Let's walk through the process. Each time we encounter a layer on the assembly line, we make a decision: use this layer or not? The best result overall is the better of the following two outcomes:
We use this layer, and build on top of it the tallest cake using the remaining layers not larger than this layer.
We don't use this layer, and build the tallest cake using any of the remaining layers.
We can model this simply with recursion - pseudocode:
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)
However, that won't cut it by itself, since we're supposed to use dynamic programming.
The idea is that we're recursively calling tallest with other_layers both times. Wouldn't it be nice if we could call it once, and have all the information we need?
What information do we need? Well, if we had the tallest cake using the remaining layers for any base size, we'd be set: we just pick the tallest cake that can fit on the current layer, and see if that makes an improvement versus the tallest cake overall. But here's the trick: even if it doesn't make an improvement, we may still gain information. The idea is to have a list of the most "efficient" (smallest base) cakes for each size.
Our process will therefore be as follows:
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.
This input sequence is tricky:
10 45 25 40 38 20 10 32 25 18 30
A simplistic approach that only skips lead-in layers would find these [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]
The rules of the game allow you to skip any layer, though, not just the lead ones, and so the correct tallest cake in this case would be:
10 [45] 25 [40] [38] 20 10 [32] [25] [18] 30
Or written out with only the selected layers:
45 40 38 32 25 18
The problem you're trying to solve is a dynamic programming question (albeit a simple one).
Algorithm
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;
}
Step By Step
As a step through of how this works, consider:
8 16 12 6 6 10 5
Since we are going in reverse order,
5 10 6 6 12 16 8
Starting with 5, there are smaller than 5 values from []:
5 10 6 6 12 16 8
1
From [5], max[5] = 1 so 1+1
5 10 6 6 12 16 8
1 2
And so on...
5 10 6 6 12 16 8
1 2 2 3 4 5 4
Then we find out max of the list [1, 2, 2, 3, 4, 5, 4], which is 5.
And, as explained above, this is the correct answer to the provided example that he stepped through in the problem description.
How It Works
The algorithm works by taking saving the maximum value for each layer. The question explains that, for any given layer, it can stack only cakes less than or equal to its diameter. Therefore, the maximum of any given layer will always be the maximum of a layer of equal or less size which follows it on the belt plus 1 (counting the layer itself). If there are no layers that can be stacked onto it, we know that the max for this layer is 1.
Actually is very simple, it goes like this:
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));

Categories