TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK Lehrstuhl für Sprachen und Beschreibungsstrukturen Einführung in die Informatik I für Ingenieure Prof. Dr. Helmut Seidl, A. Flexeder, S. Friese, M. Petter WS 2010/11 Übungsblatt 6 24.11.10 Abgabe: 05.12.10 (vor 12 Uhr) Aufgabe 6.1 (P) Inverse Telefonbuchsuche Es seien zwei Arrays a und b gegeben. Array a enthält Namen (Datentyp String). Array b speichert zu jedem Namen eine Telefonnummer (Datentyp int). Die Telefonnummer zu einem Namen befindet sich an der selben Position in Array b wie der Name in Array a. Schreiben Sie ein Programm, das beide Arrays aufsteigend nach der Telefonnummer sortiert. Schaffen Sie die Möglichkeit, dass der Benutzer mit Hilfe der Telefonnummer nach einem Namen suchen kann. Implementieren Sie dazu eine binäre Suche auf dem sortierten Telefonbuch. Aufgabe 6.2 (P) Türme von Hanoi In dieser Aufgabe lösen wir das Rätsel der Türme von Hanoi : Versetze einen Turm, der aus n unterschiedlich grossen Scheiben besteht, von einem Startplatz zu einem Zielplatz. Für das Versetzen eines Turms steht ein Zwischenlager zur Verfügung. Pro Zug darf nur eine Scheibe bewegt, d.h. von einem Platz zu einem anderen Platz verlegt werden. Zudem darf niemals eine grössere Scheibe auf eine kleinere Scheibe gelegt werden! Schreiben Sie ein Programm, welches der Reihe nach Anweisungen ausgibt, von welchem Stapel man eine Scheibe auf welchen anderen Stapel bewegen muß, wenn man einen Turm nach obigen Regeln versetzen will. Für eine einzelne Scheibe ist die Lösung ganz einfach. Die Lösungsstrategie für einen Stapel der Höhe n besteht darin, zuerst davon auszugehen, dass man Türme der Höhe n − 1 bereits regelgerecht versetzen kann, und darauf aufbauend dann eine Lösung für Türme der Höhe n zu konstruieren. Beispiel: a) Wir wollen den Turm von Lager 1 nach Lager 2 bewegen. Als Zwischenlager wählen wir 3. b) Bewege n − 1 Scheiben vom Startplatz (1) zum Zwischenlager (3). Verwende dazu den Zielplatz (2) temporär als Zwischenlager. c) Bewege die ehemals unterste Scheibe vom Startplatz (1) zum Zielplatz (2), und gib diese Aktion via write aus. d) Bewege nun die n − 1 Scheiben vom Zwischenlager (3) zum Zielplatz (2). Verwende dazu den Startplatz (1) als temporäres Zwischenlager. Wenden Sie dieses Verfahren rekursiv auf Türme an, die aus mehr als einer Scheibe bestehen. Definieren Sie dazu eine Methode void versetze(int n, int startplatz, int zielplatz, int zwischenlager), die einen Turm der Höhe n von einem Startplatz startplatz über ein Zwischenlager zwischenlager zu einem Zielplatz zielplatz versetzt. 2 Aufgabe 6.3 [4 Punkte] (H) Schatzkiste Ein Schatzgräber hat eine Schatztruhe voller wertvoller Schätze entdeckt. Leider kann er die Truhe nicht alleine schleppen und muss so die Schätze in seinem Rucksack verstauen. Jedes der gefundenen Schmuckstücke hat ein bestimmtes Gewicht und einen bestimmten Wert. Der Schatzgräber möchte seinen Rucksack nun so packen, dass der Rucksackinhalt das Maximalgewicht, das der Schatzsucher schleppen kann, nicht überschreitet und er durch die mitgenommenen Schmuckstücke einen maximalen Wert erzielt. Implementieren Sie ein optimales Packen des Rucksacks unter Verwendung rekursiver Funktionsaufrufe. Geben Sie am Ende Ihres Programms den Inhalt des gefüllten Rucksacks, sowie dessen tatsächliches Gewicht und seinen Wert aus. Das Gewicht und den Wert der einzelnen Schmuckstücke der Schatztruhe, liefert Ihnen die Methode int[][] getTreasure(int n) der Klasse Schatz, wobei in der ersten Spalte des n×2 Arrays das Gewicht des Schmuckstücks und in der zweiten Spalte dessen Wert abgelegt ist. Der Parameter int n gibt die Anzahl der gefundenen Schmuckstücke der Schatztruhe an. Schreiben Sie ein Programm Rucksack mit einer main-Methode, welche für einen Schatz den Wert und den Inhalt des Rucksacks ausgibt. Aufgabe 6.4 [6 Punkte] (H) Sudoku Schreiben Sie ein Programm namens Sudoku.java, welches das Spiel Sudoku implementiert. Die Grundregeln dieses Spiels sind folgende: Ein Sudoku-Feld besteht aus 9 × 9 Feldern, die zusätzlich in 3 × 3 Blöcke mit 3 × 3 Feldern aufgeteilt sind. Ausgangspunkt ist ein Sudoku-Feld in dem bereits Zahlen eingetragen sind. Ziel ist es, jede leere Zelle mit einer Zahl von 1 bis 9 so zu füllen, dass folgende Invariante gilt: Invariante: Jede Zeile, jede Spalte und jeder Block enthält jeweils alle Zahlen von 1 bis 9 höchstens genau einmal. (*) Verwenden Sie die Klasse SudokuProvider.java, die Ihnen die statische Methode int[][] fuelleFeld(int n) zur Verfügung stellt. Der Rückgabewert dieser Methode ist ein 2dimensionales Array, welches ein Sudoku-Feld repräsentiert, wobei Zellen entweder unbelegt sind (d.h. den Wert 0 haben) oder bereits mit einer Zahl zwischen 1 und 9 unveränderlich vorbelegt sind. Der Parameter n bedeutet, dass das Sudoku-Feld mit n zufälligen Zahlen in zufälligen Feldern gefüllt werden soll. Gehen Sie zur Implementierung von Sudoku wie folgt vor: a) Definieren Sie eine Funktion boolean testeFeld(int[][] sudoku, int i, int j), die false zurückgibt, falls der Eintrag in Zelle (i, j) zur Verletzung der Invariante (*) führt (d.h. dass der Wert in Zelle (i, j) in der i-ten Spalte, j-ten Zeile oder in dem jeweiligen Block ein weiteres Mal vorkommt). b) Schreiben Sie eine Funktion loeseFeld(int[][] feld, int i, int j), die versucht die Zelle (i, j) im Feld feld der Reihe nach mit Einträgen von 1 bis 9 zu füllen. Sobald ein gültiger Eintrag gefunden wurde, soll loeseFeld rekursiv für die nächste Zelle aufgerufen werden. Falls loeseFeld false zurückgibt, sollen die eventuell verbleibenden Möglichkeiten, um Zelle (i, j) zu belegen, durchprobiert werden (inkl. rekursivem Abstieg). Falls das nicht zum Erfolg führt, soll (i, j) wieder auf 0 zurückgesetzt und false zurückgeben werden. Beachten Sie dabei die vorgegebenen unveränderlichen Zellen, die Methode fuelleFeld bereits vorbelegt hat! Geben Sie am Ende das urspüngliche Sudoku-Feld und, falls möglich, eine Lösung aus. c) Erweitern Sie Sudoku so, dass es die Anzahl der verschiedenen Lösungen ausgibt.