#include <stdio.h>
#include <stdlib.h>
// Teil 1a: Maximale Dimensionen definieren
#define NMAX 8
#define MMAX 8
// Teil 1b: Struktur Feld
typedef struct {
int iN; // Anzahl der Zeilen
int iM; // Anzahl der Spalten
char archFeld[NMAX][MMAX]; // Zweidimensionales Array
} Feld;
// Teil 1c: Struktur Tile
typedef struct {
unsigned char chFarbCode; // Farbcode der Tiles
int iAnzFelder; // Anzahl der elementaren Felder
int iAnzRotationen; // Anzahl der gültigen Rotationen
Feld arstRotationen[4]; // Array für max. 4 Rotationen
} Tile;
// Teil 3l: Struktur TileList für verkettete Liste
typedef struct TileList {
char chTileIndex; // Index der Tile
char chRotIndex; // Index der Rotation
char chXPos; // X-Position
char chYPos; // Y-Position
struct TileList *pstPrev; // Zeiger auf vorheriges Element
} TileList;
// Globale Variable für die beste Tile-Liste
static TileList *pstBestTileList = NULL;
// Teil 1e: Funktion zum Ausgeben aller Tiles
void PrtTiles(Tile arstTiles[]) {
printf("Zur Verfuegung stehende Tiles:\n");
// Für jeden Tile-Typ
for (int i = 0; i < 4; i++) {
printf("Tile %d -- Rotation(en): %d; Felder: %d\n",
i + 1, arstTiles[i].iAnzRotationen, arstTiles[i].iAnzFelder);
// Finde maximale Höhe über alle Rotationen
int maxHoehe = 0;
for (int rot = 0; rot < arstTiles[i].iAnzRotationen; rot++) {
if (arstTiles[i].arstRotationen[rot].iN > maxHoehe) {
maxHoehe = arstTiles[i].arstRotationen[rot].iN;
}
}
// Ausgabe zeilenweise
for (int z = 0; z < maxHoehe; z++) {
// Für jede Rotation
for (int rot = 0; rot < arstTiles[i].iAnzRotationen; rot++) {
Feld *pstRot = &arstTiles[i].arstRotationen[rot];
// Wenn diese Zeile für diese Rotation existiert
if (z < pstRot->iN) {
for (int s = 0; s < pstRot->iM; s++) {
if (pstRot->archFeld[z][s] != 0) {
printf("%c%c", arstTiles[i].chFarbCode, arstTiles[i].chFarbCode);
} else {
printf(" ");
}
}
} else {
// Leere Zeile für diese Rotation
for (int s = 0; s < pstRot->iM; s++) {
printf(" ");
}
}
// 4 Leerzeichen zwischen Rotationen (außer nach der letzten)
if (rot < arstTiles[i].iAnzRotationen - 1) {
printf(" ");
}
}
printf("\n");
}
}
}
// Teil 1g: Funktion zum Ausgeben des Feldes
void PrtFeld(Feld *pstAktFeld, Tile arstTiles[]) {
printf("\nZu partitionierende Flaeche:\n");
// Obere Begrenzung
printf("\xDA"); // ┌
for (int i = 0; i < pstAktFeld->iM * 2; i++) {
printf("\xC4"); // ─
}
printf("\xBF\n"); // ┐
// Inhalt
for (int z = 0; z < pstAktFeld->iN; z++) {
printf("\xB3"); // │
for (int s = 0; s < pstAktFeld->iM; s++) {
char tileIndex = pstAktFeld->archFeld[z][s];
if (tileIndex == 0) {
printf(" ");
} else {
// Tile-Index auf Array-Index umrechnen (1-basiert zu 0-basiert)
unsigned char farbCode = arstTiles[tileIndex - 1].chFarbCode;
printf("%c%c", farbCode, farbCode);
}
}
printf("\xB3\n"); // │
}
// Untere Begrenzung
printf("\xC0"); // └
for (int i = 0; i < pstAktFeld->iM * 2; i++) {
printf("\xC4"); // ─
}
printf("\xD9\n"); // ┘
}
// Teil 2i1: Funktion zum Zählen der Felder
int AnzFelder(char archFeld[NMAX][MMAX]) {
int anzahl = 0;
for (int z = 0; z < NMAX; z++) {
for (int s = 0; s < MMAX; s++) {
if (archFeld[z][s] != 0) {
anzahl++;
}
}
}
return anzahl;
}
// Teil 2i2: ORIGINALE Tilify-Funktion (NICHT OPTIMIERT)
void Tilify(Feld *pstAktFeld, Tile arstTiles[], int iAnzFelder, int iAnzTiles) {
static int iMinAnzTiles = 999999; // Große Zahl für Initialisierung
// Basisfall: Alle Felder sind belegt
if (iAnzFelder <= 0) {
if (iAnzTiles < iMinAnzTiles) {
iMinAnzTiles = iAnzTiles;
printf("Neue beste Loesung mit %d Tiles gefunden!\n", iAnzTiles);
}
return;
}
// Für alle Tiles
for (int t = 0; t < 4; t++) {
// Für alle Rotationen
for (int r = 0; r < arstTiles[t].iAnzRotationen; r++) {
Feld *pstRot = &arstTiles[t].arstRotationen[r];
// Für alle Positionen im Feld
for (int z = 0; z <= pstAktFeld->iN - pstRot->iN; z++) {
for (int s = 0; s <= pstAktFeld->iM - pstRot->iM; s++) {
// Prüfe ob Tile passt
int passt = 1;
for (int tz = 0; tz < pstRot->iN && passt; tz++) {
for (int ts = 0; ts < pstRot->iM && passt; ts++) {
if (pstRot->archFeld[tz][ts] != 0 &&
pstAktFeld->archFeld[z + tz][s + ts] == 0) {
passt = 0;
}
}
}
if (passt) {
// Kopiere Feld
Feld stFeldProc = *pstAktFeld;
// Setze Tile-Felder auf 0
for (int tz = 0; tz < pstRot->iN; tz++) {
for (int ts = 0; ts < pstRot->iM; ts++) {
if (pstRot->archFeld[tz][ts] != 0) {
stFeldProc.archFeld[z + tz][s + ts] = 0;
}
}
}
// Rekursiver Aufruf
Tilify(&stFeldProc, arstTiles,
iAnzFelder - arstTiles[t].iAnzFelder,
iAnzTiles + 1);
}
}
}
}
}
}
// Teil 3k: OPTIMIERTE Tilify_Opt-Funktion mit Speicherung
TileList* Tilify_Opt(Feld *pstAktFeld, Tile arstTiles[], int iAnzFelder, int iAnzTiles, TileList *pstTileList) {
static int iMinAnzTiles = 999999; // Große Zahl für Initialisierung
static int bInitialized = 0;
// Reset bei erstem Aufruf
if (!bInitialized) {
iMinAnzTiles = 999999;
pstBestTileList = NULL;
bInitialized = 1;
}
// Basisfall: Alle Felder sind belegt
if (iAnzFelder <= 0) {
if (iAnzTiles < iMinAnzTiles) {
iMinAnzTiles = iAnzTiles;
pstBestTileList = pstTileList;
printf("Neue beste Loesung mit %d Tiles gefunden!\n", iAnzTiles);
}
return pstTileList;
}
// Optimierung 1: Wenn aktuelle Anzahl bereits >= Minimum, abbrechen
if (iAnzTiles >= iMinAnzTiles) {
return pstTileList;
}
// Optimierung 2: Finde erstes freies Feld (von oben links)
int firstZ = -1, firstS = -1;
for (int z = 0; z < pstAktFeld->iN && firstZ == -1; z++) {
for (int s = 0; s < pstAktFeld->iM && firstZ == -1; s++) {
if (pstAktFeld->archFeld[z][s] != 0) {
firstZ = z;
firstS = s;
}
}
}
if (firstZ == -1) return pstTileList; // Kein freies Feld gefunden
// Optimierung 3: Größere Tiles zuerst probieren
for (int t = 3; t >= 0; t--) {
// Für alle Rotationen
for (int r = 0; r < arstTiles[t].iAnzRotationen; r++) {
Feld *pstRot = &arstTiles[t].arstRotationen[r];
// Prüfe alle Positionen, wo das erste Feld des Tiles das gefundene freie Feld überdecken könnte
for (int tz = 0; tz < pstRot->iN; tz++) {
for (int ts = 0; ts < pstRot->iM; ts++) {
if (pstRot->archFeld[tz][ts] == 0) continue; // Dieses Feld des Tiles ist leer
// Position wo das Tile platziert werden müsste
int z = firstZ - tz;
int s = firstS - ts;
// Prüfe ob Position gültig ist
if (z < 0 || s < 0 || z + pstRot->iN > pstAktFeld->iN || s + pstRot->iM > pstAktFeld->iM) {
continue;
}
// Prüfe ob Tile passt
int passt = 1;
for (int pz = 0; pz < pstRot->iN && passt; pz++) {
for (int ps = 0; ps < pstRot->iM && passt; ps++) {
if (pstRot->archFeld[pz][ps] != 0 &&
pstAktFeld->archFeld[z + pz][s + ps] == 0) {
passt = 0;
}
}
}
if (passt) {
// Kopiere Feld
Feld stFeldProc = *pstAktFeld;
// Setze Tile-Felder auf 0
for (int pz = 0; pz < pstRot->iN; pz++) {
for (int ps = 0; ps < pstRot->iM; ps++) {
if (pstRot->archFeld[pz][ps] != 0) {
stFeldProc.archFeld[z + pz][s + ps] = 0;
}
}
}
// Teil 3n: Neues Listenelement erstellen
TileList *pstNewTile = (TileList*)malloc(sizeof(TileList));
if (pstNewTile == NULL) {
printf("Fehler bei Speicherallokation!\n");
return pstBestTileList;
}
// n2: Member zuweisen
pstNewTile->chTileIndex = t + 1; // 1-basiert speichern
pstNewTile->chRotIndex = r;
pstNewTile->chXPos = s;
pstNewTile->chYPos = z;
// n3: pstPrev auf bisherige Liste
pstNewTile->pstPrev = pstTileList;
// Rekursiver Aufruf
Tilify_Opt(&stFeldProc, arstTiles,
iAnzFelder - arstTiles[t].iAnzFelder,
iAnzTiles + 1, pstNewTile);
}
}
}
}
}
return pstBestTileList;
}
int main() {
// Teil 1d: Definition der Tiles mit Listeninitialisierung
Tile arstTiles[4] = {
// Tile 1: 1x1
{
.chFarbCode = 0xB0, // ░
.iAnzFelder = 1,
.iAnzRotationen = 1,
.arstRotationen = {
{
.iN = 1, .iM = 1,
.archFeld = {{1}}
}
}
},
// Tile 2: L-Form mit 3 Feldern
{
.chFarbCode = 0xB1, // ▒
.iAnzFelder = 3,
.iAnzRotationen = 4,
.arstRotationen = {
{ // Rotation 1
.iN = 2, .iM = 2,
.archFeld = {{1,1},{0,1}}
},
{ // Rotation 2
.iN = 2, .iM = 2,
.archFeld = {{0,1},{1,1}}
},
{ // Rotation 3
.iN = 2, .iM = 2,
.archFeld = {{1,0},{1,1}}
},
{ // Rotation 4
.iN = 2, .iM = 2,
.archFeld = {{1,1},{1,0}}
}
}
},
// Tile 3: 1x3 / 3x1
{
.chFarbCode = 0xB2, // ▓
.iAnzFelder = 3,
.iAnzRotationen = 2,
.arstRotationen = {
{ // Horizontal
.iN = 1, .iM = 3,
.archFeld = {{1,1,1}}
},
{ // Vertikal
.iN = 3, .iM = 1,
.archFeld = {{1},{1},{1}}
}
}
},
// Tile 4: 2x2
{
.chFarbCode = 0xDB, // █
.iAnzFelder = 4,
.iAnzRotationen = 1,
.arstRotationen = {
{
.iN = 2, .iM = 2,
.archFeld = {{1,1},{1,1}}
}
}
}
};
// Teil 1f: Definition der zu parkettierenden Fläche
// Hinweis: Für Tests mit 4x4 NMAX und MMAX auf 4 setzen und diese Fläche verwenden:
/*
Feld stFeld = {
.iN = 4,
.iM = 4,
.archFeld = {
{1,1,0,0}, // Zeile 1: 2 Felder
{0,1,1,0}, // Zeile 2: 2 Felder
{0,1,1,1}, // Zeile 3: 3 Felder
{1,1,1,1} // Zeile 4: 4 Felder
}
};
*/
// Fläche aus Abbildung 4 (Ihre Fläche)
Feld stFeld = {
.iN = NMAX,
.iM = MMAX,
.archFeld = {
{0,0,0,0,0,0,0,0}, // Zeile 1: 0 Felder
{0,0,0,1,1,1,1,0}, // Zeile 2: 4 Felder (Spalten 4-7)
{0,1,1,1,1,1,0,1}, // Zeile 3: 6 Felder (Spalten 2-6 und 8)
{0,1,1,0,0,1,1,0}, // Zeile 4: 4 Felder (Spalten 2-3 und 6-7)
{0,1,1,1,1,1,1,0}, // Zeile 5: 6 Felder (Spalten 2-7)
{0,1,1,1,1,1,1,0}, // Zeile 6: 6 Felder (Spalten 2-7)
{0,0,0,1,1,0,0,0}, // Zeile 7: 2 Felder (Spalten 4-5)
{0,0,0,0,0,0,0,0} // Zeile 8: 0 Felder
}
};
// Ausgabe der Tiles
PrtTiles(arstTiles);
// Ausgabe der initialen Fläche
PrtFeld(&stFeld, arstTiles);
// Teil 3m: Initialisierung der Tile-Liste
TileList *pstTileList = NULL;
// Anzahl der zu parkettierenden Felder
int anzFelder = AnzFelder(stFeld.archFeld);
printf("\nAnzahl zu parkettierender Felder: %d\n", anzFelder);
// Teil 2i3: Aufruf der NICHT-optimierten Tilify für Teil 2
// Hinweis: Diese Funktion ist sehr langsam für große Flächen!
// Verwenden Sie die Testfläche 4x4 für diese Funktion
/*
printf("\nSuche mit nicht-optimierter Tilify-Funktion...\n");
Tilify(&stFeld, arstTiles, anzFelder, 0);
*/
// Teil 3: Optimale Parkettierung mit optimierter Funktion finden
printf("\nSuche optimale Parkettierung...\n");
printf("(Dies kann einen Moment dauern...)\n");
// Teil 3n7: Erweiterte Funktion gibt Zeiger zurück
TileList *pstOptimalList = Tilify_Opt(&stFeld, arstTiles, anzFelder, 0, pstTileList);
// Teil 3o: Optimale Parkettierung anwenden
if (pstBestTileList != NULL) {
// o1: Neue Fläche erstellen
Feld stOptFeld = stFeld;
// o2: Tiles aus der Liste auf die Fläche legen
TileList *pstCurrent = pstBestTileList;
int tileCount = 0;
// Sicherheitscheck
while (pstCurrent != NULL && pstCurrent != (TileList*)0xFFFFFFFFFFFFFFFF) {
// Weitere Sicherheitschecks
if ((void*)pstCurrent < (void*)0x1000) {
printf("Fehler: Ungültiger Zeiger!\n");
break;
}
int tileIdx = pstCurrent->chTileIndex - 1; // Auf 0-basiert umrechnen
int rotIdx = pstCurrent->chRotIndex;
int xPos = pstCurrent->chXPos;
int yPos = pstCurrent->chYPos;
// Validierung der Indizes
if (tileIdx < 0 || tileIdx >= 4 || rotIdx < 0 || rotIdx >= 4 ||
xPos < 0 || xPos >= MMAX || yPos < 0 || yPos >= NMAX) {
printf("Fehler: Ungültige Tile-Parameter!\n");
break;
}
Feld *pstRot = &arstTiles[tileIdx].arstRotationen[rotIdx];
// Tile auf die Fläche setzen
for (int z = 0; z < pstRot->iN; z++) {
for (int s = 0; s < pstRot->iM; s++) {
if (pstRot->archFeld[z][s] != 0) {
if (yPos + z < NMAX && xPos + s < MMAX) {
stOptFeld.archFeld[yPos + z][xPos + s] = tileIdx + 1;
}
}
}
}
tileCount++;
pstCurrent = pstCurrent->pstPrev;
// Endlosschleife verhindern
if (tileCount > 100) {
printf("Fehler: Zu viele Tiles!\n");
break;
}
}
// o3: Optimale Parkettierung ausgeben
printf("\nOptimale Partitionierung (%d Tiles):\n", tileCount);
// Ausgabe mit spezieller Überschrift
printf("\xDA"); // ┌
for (int i = 0; i < stOptFeld.iM * 2; i++) {
printf("\xC4"); // ─
}
printf("\xBF\n"); // ┐
// Inhalt
for (int z = 0; z < stOptFeld.iN; z++) {
printf("\xB3"); // │
for (int s = 0; s < stOptFeld.iM; s++) {
char tileIndex = stOptFeld.archFeld[z][s];
if (tileIndex == 0) {
printf(" ");
} else {
unsigned char farbCode = arstTiles[tileIndex - 1].chFarbCode;
printf("%c%c", farbCode, farbCode);
}
}
printf("\xB3\n"); // │
}
// Untere Begrenzung
printf("\xC0"); // └
for (int i = 0; i < stOptFeld.iM * 2; i++) {
printf("\xC4"); // ─
}
printf("\xD9\n"); // ┘
// Teil 1p: Antwort auf die Frage
printf("\nFuer die Parkettierung der Flaeche werden mindestens %d Tiles benoetigt.\n", tileCount);
} else {
printf("\nKeine Loesung gefunden!\n");
}
// Teil 2h: Antwort auf die Frage zur Parameterübergabe
printf("\n== Antwort zu Aufgabe 2h ==\n");
printf("Die Ubergabe des Parameters arstTiles erfolgt als Array-Parameter.\n");
printf("In C werden Arrays immer als Zeiger auf das erste Element uebergeben.\n");
printf("Daher steht der gesetzte Wert auch in der aufrufenden Funktion zur Verfuegung.\n");
// Teil 3j: Antwort zu Optimierungsmaßnahmen
printf("\n== Antwort zu Aufgabe 3j ==\n");
printf("Optimierungsmassnahmen:\n");
printf("1. Abbruch wenn aktuelle Anzahl >= bisheriges Minimum (Branch & Bound)\n");
printf("2. Immer das erste freie Feld abdecken (verhindert Lochbildung)\n");
printf("3. Groessere Tiles zuerst probieren (bessere Abdeckung)\n");
return 0;
}