From 15bc156b02145aa112db8484c57544522c5da900 Mon Sep 17 00:00:00 2001 From: Manuel Bucher <manuel.bucher@hu-berlin.de> Date: Fri, 22 Jun 2018 00:23:42 +0200 Subject: [PATCH] added new solution --- Makefile | 6 +- loesung.c | 163 +++++++++++++++++++----- loesung2.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 491 insertions(+), 35 deletions(-) create mode 100755 loesung2.c diff --git a/Makefile b/Makefile index 742516b..ebb9bf3 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ -all: loesung +all: loesung2 WARNINGS = -Wall #-Werror OPTIONAL = -Wextra -Wpedantic -Wshadow DEBUG = -g -ggdb -fno-omit-frame-pointer OPTIMIZE = -Og -std=c11 -loesung: Makefile loesung.c - $(CC) -o $@ $(WARNINGS) $(OPTIONAL) $(DEBUG) $(OPTIMIZE) loesung.c +loesung: Makefile loesung2.c + $(CC) -o $@ $(WARNINGS) $(OPTIONAL) $(DEBUG) $(OPTIMIZE) loesung2.c clean: rm -f loesung diff --git a/loesung.c b/loesung.c index cbcc3d2..0f10c42 100644 --- a/loesung.c +++ b/loesung.c @@ -4,31 +4,32 @@ #include <stdint.h> // fixed sized integer #include <stdbool.h> // booleans +#define INF UINT32_MAX + typedef struct Tile { uint32_t x; uint32_t y; - struct Tile *p_next; // used for list - struct Tile *p_n; // north + struct Tile *p_matching; // matching partner + struct Tile *p_next; // used for list and queue + struct Tile *p_n; // north has to be first struct Tile *p_e; // east struct Tile *p_s; // south struct Tile *p_w; // west + uint32_t dist; } Tile; // Reservieren und initialisieren des Speicherplatz eines Tiles -Tile* tile_new() { - Tile *p_tile = malloc(sizeof(Tile)); - if(p_tile == NULL) { // failed to allocate memory - return NULL; - } - p_tile->x = 0; - p_tile->y = 0; - p_tile->p_next = NULL; - p_tile->p_n = NULL; - p_tile->p_e = NULL; - p_tile->p_s = NULL; - p_tile->p_w = NULL; - return p_tile; +void tile_init(Tile* this) { + this->x = 0; + this->y = 0; + this->p_matching = NULL; + this->p_next = NULL; + this->p_n = NULL; + this->p_e = NULL; + this->p_s = NULL; + this->p_w = NULL; + this->dist = INF; } int tile_compare(const void *p, const void *q) { @@ -47,6 +48,9 @@ int tile_compare(const void *p, const void *q) { return 0; // Elemente sind gleich } +inline bool tile_is_odd(Tile* this) { + return ((this->x ^ this->y)&1) != 0; +} /*************** List von Tiles *********************/ /** List von Tiles */ @@ -87,7 +91,7 @@ void list_push(List *this, Tile *p_tile) { this->max.x = max(this->max.x, p_tile->x); this->max.y = max(this->max.y, p_tile->y); this->num_tiles++; - this->num_odd += (p_tile->x - p_tile->y)&1; + this->num_odd += tile_is_odd(p_tile); } /** Freigeben des reservierten Speichers von der Liste @@ -144,6 +148,51 @@ void list_print_rectangle(List *this) { } } +/** Schlange von Tiles */ +typedef struct Queue { + Tile *p_first; + Tile *p_last; +} Queue; + +void queue_init(Queue* this) { + this->p_first = NULL; + this->p_last = NULL; +} + +void queue_push(Queue* this, Tile* p_tile) { + if (this->p_last != NULL) { + this->p_last->p_next = p_tile; + } else { + this->p_first = p_tile; + } + this->p_last = p_tile; + p_tile->p_next = NULL; +} + +inline Tile *queue_first(Queue* this) { + return this->p_first; +} + +inline bool queue_empty(Queue* this) { + return this->p_first == NULL; +} + +Tile *queue_pop(Queue* this) { + Tile *p_tile = this->p_first; + this->p_first = p_tile->p_next; + if (this->p_first == NULL) { + this->p_last = NULL; + } + return p_tile; +} + +/** Parsen einer Kachel(Tile) aus einer Zeile von StdIn + * + * Die Zahlen weReaden in das uebergebene Struct pTile geschrieben, sind aber + * nur gueltig, wenn der Rueckgabewert `Ok` ist. + * + * Voraussetzung: p_tile muss initialisiert sein + */ typedef enum ReadResult { ReadOk, ReadEof, @@ -153,13 +202,6 @@ typedef enum ReadResult { ReadErrTooManyNumbers, ReadErrOutOfMemory, } ReadResult; -/** Parsen einer Kachel(Tile) aus einer Zeile von StdIn - * - * Die Zahlen weReaden in das uebergebene Struct pTile geschrieben, sind aber - * nur gueltig, wenn der Rueckgabewert `Ok` ist. - * - * Voraussetzung: p_tile muss initialisiert sein - */ ReadResult read_line(Tile* p_tile){ int c = getchar(); if (c == EOF) { @@ -225,10 +267,11 @@ ReadResult read_line(Tile* p_tile){ */ ReadResult parse_input(List *this) { while (1) { - Tile *p_tile = tile_new(); // neues Element erstellen + Tile *p_tile = malloc(sizeof(Tile)); if (p_tile == NULL) { return ReadErrOutOfMemory; } + tile_init(p_tile); ReadResult result = read_line(p_tile); switch (result) { case ReadOk: @@ -252,6 +295,7 @@ ReadResult parse_input(List *this) { */ typedef struct Array { uint32_t num_tiles; + Tile null; Tile* (*tiles); } Array; @@ -259,7 +303,7 @@ typedef struct Array { * inklusive der Tiles */ void array_drop(Array *this) { if(this->tiles != NULL) { - for(uint32_t i = 0; i < this->num_tiles; i++) { + for(uint32_t i = 1; i < this->num_tiles; i++) { free(this->tiles[i]); } free(this->tiles); @@ -275,13 +319,15 @@ typedef enum MallocResult { * Laufzeit: O(n) */ MallocResult array_from_list(Array *this, List* p_list) { - this->tiles = malloc(p_list->num_tiles * sizeof(Tile*)); + this->num_tiles = p_list->num_tiles+1; + this->tiles = malloc(this->num_tiles * sizeof(Tile*)); if(this->tiles == NULL) { return MallocFailed; } - this->num_tiles = p_list->num_tiles; + tile_init(&this->null); // create null tile for hk + this->tiles[0] = &this->null; Tile *cur = p_list->p_top; - for (uint32_t i = 0; i < this->num_tiles; i++) { + for (uint32_t i = 1; i < this->num_tiles; i++) { this->tiles[i] = cur; cur = cur->p_next; this->tiles[i]->p_next = NULL; @@ -296,9 +342,9 @@ MallocResult array_from_list(Array *this, List* p_list) { * 1 falls Dopplungen vorkommen TODO: enum */ int array_find_neighbours(Array *this) { - uint32_t i = 0; // vergleich mit oberer Zeile - uint32_t begin_line = 0; - for (uint32_t j = 1; j < this->num_tiles; j++) { + uint32_t i = 1; // vergleich mit oberer Zeile + uint32_t begin_line = 1; + for (uint32_t j = 2; j < this->num_tiles; j++) { Tile *p_cur = this->tiles[j]; Tile *p_east = this->tiles[j-1]; // ermitteln der ost/west-Beziehung @@ -337,6 +383,57 @@ void array_print(Array *this) { } } +/* algorithmus von Hopcroft und Karp */ +bool hk_bfs(Array *this) { + Queue q; + queue_init(&q); // queue von odd Tiles + + for (int i = 0; i < this->num_tiles; i++) { + Tile *p_cur = this->tiles[i]; + if (tile_is_odd(p_cur)) { + continue; + } + if (p_cur->p_matching == NULL) { + // Freier Knoten -> Zur Schlange hinzufuegen + p_cur->dist = 0; // Erste Spalte im Schichtgraphen + queue_push(&q, p_cur); + } else { + p_cur->dist = INF; // Spaetere Spalte im Schichtgraphen (alle Kanten?) + } + } + this->null.dist = INF; + while (!queue_empty(&q)) { + Tile *p_cur = queue_pop(&q); + if (p_cur->dist < INF) { + for (int i = 0; i < 4; i++) { + Tile *p_neigh = p_cur->p_n+i; + if (p_neigh == NULL) + continue; + // Kante noch nicht im Schichtgraphen? + if(p_neigh->dist == INF) { + Tile *matching = p_neigh->p_matching; + if (matching == NULL) { + return true; // Es gibt einen augmentierenden Pfad + } + matching->dist = p_cur->dist + 1; + queue_push(&q, matching); + } + } + } + } + return false; +} + +bool hk_dfs(Tile *this) { + if (this == NULL) + return true; + + for (int i = 0; i < 4; i++) { + Tile *p_neigh = this->p_n+i; // Benachbart zu odd + } + return false; +} + int main (void) { // erstellen des Liste zum parsen des inputs @@ -383,7 +480,8 @@ int main (void) return EXIT_FAILURE; } - qsort(tile_array.tiles, tile_array.num_tiles, sizeof(Tile*), tile_compare); + // ignoriert null-tile + qsort(tile_array.tiles, tile_array.num_tiles-1, sizeof(Tile*), tile_compare); if(array_find_neighbours(&tile_array)) { fprintf(stderr, "Error: Duplicated entry\n"); @@ -407,6 +505,7 @@ int main (void) // Algorithmus von Hopcroft und Karp + array_drop(&tile_array); return EXIT_SUCCESS; } diff --git a/loesung2.c b/loesung2.c new file mode 100755 index 0000000..a7ddbe3 --- /dev/null +++ b/loesung2.c @@ -0,0 +1,357 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> // fixed sized integer +#include <stdbool.h> // booleans + +#define INF UINT32_MAX + +/** Min-Max funktionen fuer uint32_t */ +inline uint32_t min(uint32_t a, uint32_t b) { return a < b ? a : b; } +inline uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } + +/********** Kachel definition **********/ +typedef struct Tile { + uint32_t x; + uint32_t y; + uint32_t neigh[4]; // Nachbarn + struct Tile *p_next; // queue + uint32_t pair; // matching partner + uint32_t dist; +} Tile; + +// initialisieren des Speichers von einem Tile +void tile_init(Tile *this) { + memset(this, 0, sizeof(Tile)); + this->dist = INF; +} + +int tile_compare(const void *p, const void *q) { + const Tile *s = (const Tile*) p; + const Tile *t = (const Tile*) q; + // als erstes nach y sortieren + if (s->y < t->y) return -1; + if (s->y > t->y) return 1; + // danach nach x sortieren + if (s->x < t->x) return -1; + if (s->x > t->x) return 1; + return 0; // Elemente sind gleich +} + +// Kacheln wie im Schachbrettmuster einordnen +uint32_t tile_odd(Tile* this) { + return ((this->x ^ this->y)&1); +} + +/********** Kachelfeld **********/ +typedef struct Field { + // Array von Tiles, tile 0 is imagenary tile for hk-algorithmus + // Tiles werden mit 1 bis num_tiles exklusive indiziert + Tile *tiles; + uint32_t num_tiles; + uint32_t size; + uint32_t num_odd; + // TODO: Rechteck erkennen? +} Field; + +// returns 0 if successful, nonzero if failed +int field_init(Field *this) { + memset(this, 0, sizeof(Field)); + this->tiles = malloc(sizeof(Tile)); + this->num_tiles = 1; + this->size = 1; + return this->tiles == 0; +} + +// if an error occurs, all memory get freed +int field_push(Field *this, Tile *p_tile) { + if (this->size == this->num_tiles) { + if (this->size == 1u<<31) return 1; // catch overflow + this->size *= 2; + Tile *new = realloc(this->tiles, this->size*sizeof(Tile)); + if (new == NULL) return 1; // catch failed realloc + this->tiles = new; + } + this->num_odd += tile_odd(p_tile); + memcpy(this->tiles+this->num_tiles, p_tile, sizeof(Tile)); + this->num_tiles++; + return 0; +} + +/** Parsen einer Kachel(Tile) aus einer Zeile von StdIn + * + * Die Werte werden in die uebergebene Kachel geschrieben, sind aber + * nur gueltig, wenn der Rueckgabewert `ReadOk` ist. + * + * Voraussetzung: p_tile muss initialisiert sein + */ +typedef enum ReadResult { + ReadOk, + ReadEof, + ReadErrOverflow, + ReadErrInvalidChar, + ReadErrTooFewNumbers, + ReadErrTooManyNumbers, + ReadErrOutOfMemory, +} ReadResult; +ReadResult read_line(Tile* p_tile){ + int c = getchar(); + if (c == EOF) { + return ReadEof; + } + int cur_number = 0; + bool cur_whitespace = true; + uint32_t *p_cur; // aktuell zu parsende Zahl + while(1) { + if ('0' <= c && c <= '9') { + if (cur_whitespace) { + if(cur_number == 2) { + return ReadErrTooManyNumbers; + } + p_cur = (&p_tile->x)+cur_number; + cur_whitespace = false; + cur_number++; + } + // 429496730 = 2^32 / 10, daher wenn `p_cur` groesser ist, wird ein + // overflow erzeugt + if (*p_cur > 429496729) { + return ReadErrOverflow; + } + (*p_cur) *= 10; + int digit = c - '0'; + if (*p_cur > UINT32_MAX - digit) { + return ReadErrOverflow; + } + (*p_cur) += digit; + } else if (c == ' ') { + cur_whitespace = true; + } else if (c == '\n') { + if (cur_number == 2) { + return ReadOk; + } else { + return ReadErrTooFewNumbers; + } + } else { + return ReadErrInvalidChar; + } + c = getchar(); // get next character + } +} + +/** Liest das Input von StdIn und schreibt das Ergebnis in die uebergebene Liste + * + * Laufzeit: O(n) + * Speicherbedarf: O(n) + * + * Return: + * - ReadResult: + * ReadOk, falls das lesen Erfolgreich war + * ReadErr..., falls die Eingabe ungueltig ist oder zu wenig Speicher vorhanden. + * `num_tiles` indiziert die Zeile, in der der Fehler aufgetreten ist + */ +ReadResult field_parse(Field *this) { + while (1) { + Tile next; + tile_init(&next); + ReadResult result = read_line(&next); + switch (result) { + case ReadOk: + // einfuegen in die Liste + if (field_push(this, &next)) { + return ReadErrOutOfMemory; + } + break; + case ReadEof: + if (this->num_tiles == 1) { + return ReadEof; + } else { + return ReadOk; + } + default: // error + return result; + } + } +} + +/** Setzen der Nachbarn der Kacheln + * Rueckgabe: 0 falls alles ok + * 1 falls Dopplungen vorkommen + */ +int field_neighbours(Field *this) { + uint32_t top = 1; // vergleich mit oberer Zeile + uint32_t begin_line = 1; // start der aktuellen Zeile + Tile *t = this->tiles; + for (uint32_t cur = 2; cur < this->num_tiles; cur++) { + // ermitteln der ost/west-Beziehung + if (t[cur].y == t[cur-1].y) { + if (t[cur].x == t[cur-1].x) { + return 1; // Doppelung + } + if (t[cur].x == t[cur-1].x - 1) { + t[cur].neigh[1] = cur-1; + t[cur-1].neigh[3] = cur; + } + } else { + begin_line = cur; // neue Zeile + } + // ermitteln der nord/sued-Beziehung + if (t[top].y < t[cur].y - 1) { + top = begin_line; // Luecke zwischen zeilen + } else if (t[top].y == t[cur].y - 1) { + // finden, ob paar existiert, zurzeit O(n). + for(;t[top].y < t[cur].y && top < begin_line; top++); + if (t[top].x == t[cur].x) { + t[top].neigh[2] = cur; + t[cur].neigh[0] = top; + } + } + } + return 0; +} + +/********** Schlange von Tiles **********/ +typedef struct Queue { + Tile *p_first; + Tile *p_last; +} Queue; + +void queue_init(Queue* this) { + this->p_first = NULL; + this->p_last = NULL; +} + +void queue_push(Queue* this, Tile* p_tile) { + if (this->p_last != NULL) { + this->p_last->p_next = p_tile; + } else { + this->p_first = p_tile; + } + this->p_last = p_tile; + p_tile->p_next = NULL; +} + +bool queue_empty(Queue* this) { + return this->p_first == NULL; +} + +Tile *queue_pop(Queue* this) { + Tile *p_tile = this->p_first; + this->p_first = p_tile->p_next; + if (this->p_first == NULL) { + this->p_last = NULL; + } + return p_tile; +} + +/********** Algorithmus von Hopcroft und Karp **********/ +bool hk_bfs(Field *this) { + Queue q; // Schlange von Knoten + queue_init(&q); + Tile *t = this->tiles; + for (int i = 1; i < this->num_tiles; i++) { + // nur gerade Kacheln werden zur queue hinzufuegen + if (tile_odd(&t[i])) { + continue; + } + if (t[i].pair == 0) { + t[i].dist = 0; + queue_push(&q, &t[i]); + } else { + t[i].dist = INF; + } + } + t[0].dist = INF; + while (!queue_empty(&q)) { + Tile *e = queue_pop(&q); // nehme naechste gerade Kachel + if (e->dist >= t[0].dist) + continue; + for (int i = 0; i < 4; i++) { + Tile* p_neigh = &t[e->neigh[i]]; + Tile* p_pair = &t[p_neigh->pair]; + if(p_pair->dist == INF) { + p_pair->dist = e->dist+1; + queue_push(&q, p_pair); + } + } + } + return t[0].dist != INF; +} + +bool hk_dfs(Field *this, uint32_t e) { + if (e == 0) + return true; + Tile *t = this->tiles; + Tile *p_even = &t[e]; + for (int i = 0; i < 4; i++) { + // Benachbart zu e + uint32_t o = p_even->neigh[i]; // Nachbar, ungerades Tile + Tile* p_odd = &t[o]; + if (t[p_odd->pair].dist == p_even->dist+1) { + // Augmentiere den Pfad, falls durch Tiefensuche gefunden + if (hk_dfs(this, p_odd->pair)) { + printf("%u %u\n", e, o); + p_even->pair = o; + p_odd->pair = e; + return true; + } + } + } + // Kein augmentierender Pfad von e aus + p_even->dist = INF; + return false; +} + +void hk_print(Field *this) { + for (uint32_t i = 1; i < this->num_tiles; i++) { + printf("%u\n", i); + if (this->tiles[i].pair == 0) { + printf("None\n"); + return; // Kein Belegung existiert + } + } +} + +void hk(Field *this) { + uint32_t result = 0; + while(hk_bfs(this)) { + for (int i = 1; i < this->num_tiles; i++) { + if (tile_odd(&this->tiles[i])) + continue; + if (this->tiles[i].pair == 0 && hk_dfs(this, i)) + result++; + } + } + hk_print(this); +} + +int main() { + Field f; + if(field_init(&f)) { + fprintf(stderr, "Failed to allocate memory"); + return EXIT_FAILURE; + } + int result = EXIT_FAILURE; + switch(field_parse(&f)) { + case ReadOk: + qsort(f.tiles+1, f.num_tiles-1, sizeof(Tile), tile_compare); + if (field_neighbours(&f) != 0) { + fprintf(stderr, "Error: Duplicated entry\n"); // Duplikat exisitert + break; + } + /*if (f.num_tiles - f.num_odd == f.num_odd) { + printf("None\n"); + }*/ + hk(&f); // Algorithmus von Hopcroft und Karp + result = EXIT_SUCCESS; + break; + case ReadEof: result = EXIT_SUCCESS; break; + case ReadErrOverflow: fprintf(stderr, "%d: too big integer\n", f.num_tiles); break; + case ReadErrInvalidChar: fprintf(stderr, "%d: invalid character\n", f.num_tiles); break; + case ReadErrTooFewNumbers: fprintf(stderr, "%d: too few numbers\n", f.num_tiles); break; + case ReadErrTooManyNumbers: fprintf(stderr, "%d: too many numbers\n", f.num_tiles); break; + case ReadErrOutOfMemory: fprintf(stderr, "%d: failed to allocate more memory\n", f.num_tiles); break; + } + free(f.tiles); + return result; +} -- GitLab