====== Day 11: Plutonian Pebbles ======
Dieser Tag ist etwas kniffliger. Teil 1 kann man noch zur **mittleren Schwierigkeit** zählen, Teil 2 hingegen zählt definitiv zur **höchsten Schwierigkeitsstufe**, da man **Memoisation bzw. dynamische Programmierung** benötigt. Das bedeutet, dass man Zwischenergebnisse speichern muss, um bei erneuten rekursiven Aufrufen mit denselben Parametern direkt das vorherige Ergebnis zurückliefern zu können. Andernfalls würde die Rechenzeit für Teil 2 explodieren. Hierzu wird eine **HashMap** benötigt.
===== Teil 1 =====
Teil 1 lässt sich noch relativ //straight-forward// lösen. Man speichert die Input-Zahlen also in einer ArrayList und iteriert 25x über die Schleife. Pro Iteration erstellt man am besten eine **neue** ArrayList, die man mit den neuen Werten füllt, die sich nach den Regeln aus der **alten** ArrayList (des letzten Durchgangs) ergeben.
++++ Lösungsvorschlag |
public void partOne() {
// Übertrage die Input-String-Zahlen in ein Array
ArrayList input = new ArrayList();
for (String n: inputLines.get(0).split(" ")) {
input.add(Long.parseLong(n));
}
// 25x "blinzeln"
for (int blink = 0; blink < 25; blink++) {
// erstelle pro Durchgang eine neue ArrayList für die neuen Werte
ArrayList next = new ArrayList();
// pro altem Wert des letzten Durchgangs
for (long i: input) {
if (i == 0) {
next.add((long)1);
} else if (String.valueOf(i).length() % 2 == 0) {
// Aufteilung in vordere/hintere Hälfte der Zahl
next.add((long) (i / Math.pow(10,(String.valueOf(i).length()/2))));
next.add((long) (i % Math.pow(10,(String.valueOf(i).length()/2))));
} else {
next.add(i * 2024);
}
}
// mache die aktuelle/neue ArrayList "next" zur alten für den nächsten Durchlauf
input = next;
}
System.out.println(input.size());
}
++++
===== Teil 2 =====
Eine ausführliche Erklärung würde hier vermutlich den Rahmen sprengen. Der Quellcode des Lösungsvorschlags ist ein wenig kommentiert...
++++ Lösungsvorschlag |
public void partTwo() {
// Speichert pro "Steinzahl" ein Array, das wiederum pro Zeitschritt 0-74 die bereits
// errechneten Ergebnisse (Anzahl der Steine bei Zeitschritt 75) speichert.
memo = new HashMap();
// Übertrage die Input-String-Zahlen in ein Array
ArrayList input = new ArrayList();
for (String n: inputLines.get(0).split(" ")) {
input.add(Long.parseLong(n));
}
// Starte pro Input-Zahl den rekursiven Aufruf
long stones = 0;
for (long i: input) {
stones += calculate(i, 0);
}
System.out.println(stones);
}
private long calculate(long i, int times) {
// Letzter Zeitschritt? Dann gibts genau einen (weiteren) Stein
if (times == 75) {
return 1;
}
// Schaue im Speicher der Zwischenergebnisse nach, ob schon einmal die Anzahl der Steine
// beim Input i zum Zeitpunkt times berechnet wurde.
long[] stones = memo.get(i);
if (stones != null && stones[times] != 0) {
return stones[times];
} else if (stones == null) {
// Wenn zu dieser Zahl noch nie nachgeschaut wurde, dann erstelle schon mal das leere Array für die verschiedenen Zeitpunkte
stones = new long[75];
}
long result = 0;
if (i == 0) {
result += calculate((long)1,times+1);
} else if (String.valueOf(i).length() % 2 == 0) {
result += calculate((long) (i / Math.pow(10,(String.valueOf(i).length()/2))), times+1);
result += calculate((long) (i % Math.pow(10,(String.valueOf(i).length()/2))), times+1);
} else {
result += calculate(i * 2024,times+1);
}
// Speichere das neue Ergebnis für den Zeitpunkt times
stones[times] = result;
// speichere das ganze Array der verschiedenen Zeitpunkte in der HashMap passend zum Wert i
memo.put(i, stones);
return result;
}
++++