From f1b9d38007b62246b407425ce2c886ad4184cef6 Mon Sep 17 00:00:00 2001 From: Rui Ribeiro <42305006+ruiribeiro04@users.noreply.github.com> Date: Sun, 5 Oct 2025 17:10:55 +0100 Subject: [PATCH] Edited `README`'s, added `broken_gnl` exercise, `LICENSE` and main `README` --- LICENSE | 72 ++++ README.md | 125 +++++++ level-1/broken_gnl/broken_gnl.c | 97 ++++++ level-1/filter/filter.c | 84 +---- level-1/scanf/ft_scanf | 323 ------------------ .../scanf/{ft_scanf_clean.c => ft_scanf.c} | 30 +- level-2/n_queens/README.md | 220 +----------- level-2/permutations/README.md | 152 +-------- level-2/powerset/README.md | 160 +-------- level-2/rip/README.md | 148 +------- level-2/tsp/README.md | 36 ++ level-2/tsp/TSP_Algorithm_Explained.md | 247 -------------- 12 files changed, 358 insertions(+), 1336 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 level-1/broken_gnl/broken_gnl.c delete mode 100644 level-1/scanf/ft_scanf rename level-1/scanf/{ft_scanf_clean.c => ft_scanf.c} (72%) create mode 100644 level-2/tsp/README.md delete mode 100644 level-2/tsp/TSP_Algorithm_Explained.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ee86db --- /dev/null +++ b/LICENSE @@ -0,0 +1,72 @@ +# LICENSE + +## Academic Exercise Solutions Repository + +This repository contains academic exercise solutions developed as part of the École 42 curriculum. + +### Repository Contents +- **Exercise Subjects**: Intellectual property of École 42, included here only for educational context and reference. +- **Solutions & Implementations**: Original work by the repository author, licensed under terms below. + +--- + +## LICENSE TERMS + +### For the Solutions & Implementations (Original Code): + +This original code is licensed under the following terms, intended for **non-commercial use only**. + +**You are free to:** +- Use, study, and modify the code for personal, academic, or educational purposes. +- Share the code and derivative works as long as they remain non-commercial. +- Distribute your modifications under the same non-commercial license. + +**Conditions:** +- Attribution must be given to the original author (Rui Ribeiro). +- This code and any derivatives must not be used for commercial purposes. +- The license must accompany any distribution. +- Any changes must be clearly documented. +- Source code must remain openly available under these same terms. + +**Prohibited:** +- Using, distributing, or incorporating the code in any commercial, for-profit, or revenue-generating activity, + unless explicit written permission is granted by the author. +- Creating proprietary or closed-source derivatives. + +**Contact for commercial licensing or permissions:** +contacto [at] ruiribeiro [dot] me + +--- + +### For the Exercise Subjects: + +**Copyright Notice:** +The exercise subjects, problem statements, and related materials are the exclusive intellectual property of École 42. +They are included solely for **educational reference and context** within this repository. + +**Permitted Use:** +You may view and study these materials exclusively in conjunction with this repository for non-commercial academic purposes only. + +**Contact for École 42 Representatives:** +If you represent École 42 and have any concerns, requests for removal, or other issues regarding the inclusion of the exercise subjects, please contact the repository owner at: +contacto [at] ruiribeiro [dot] me + +--- + +## COPYRIGHT & ATTRIBUTION + +**Original Solutions & Implementations**: +Copyright (c) 2025 Rui Ribeiro + +**Exercise Subjects:** +Copyright École 42 + +**Repository maintained by:** Rui Ribeiro +**Contact:** contacto [at] ruiribeiro [dot] me +**Date:** October 2025 + +--- + +## DISCLAIMER + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..429ff3a --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Exam Rank 03 — 42 School: Exercise Solutions + +This repository contains solutions to the Exam Rank 03 exercises from the 42 +School curriculum. The content is organized by exercise level and includes the +exercise subjects (for reference) together with example implementations and +build scripts where applicable. + +This README provides an overview of the repository, how to build and run the +exercises, a short description of each exercise, and testing notes. + +## Repository layout + +- `level-1/` + - `broken_gnl/` — a variant of the "get_next_line" assignment (broken/intentional-bug version and subject) + - `filter/` — program that replaces occurrences of a substring with asterisks + - `scanf/` — partial implementation of `ft_scanf` + +- `level-2/` + - `n_queens/` — N-Queens solutions with Makefile and subject + - `permutations/` — generate all permutations of a string in alphabetical order + - `powerset/` — find subsets that sum to a target value + - `tsp/` — brute-force traveling salesman solver (small n) + - `rip/` — parentheses balancing and minimal removal solutions + +Each exercise directory typically contains: +- `subject.txt` or `README.md` — the exercise statement provided for reference +- `*.c`, `*.h` — implementation and headers +- `Makefile` — build helper (for many level-2 exercises) + +## Purpose and license + +The exercise subjects are the property of 42 School and are included for +educational reference. Implementations contained in this repository are the +original work of the repository author and are provided for non-commercial +educational use only (see `LICENSE` for full terms and contact information). + +## Building + +General notes: +- The projects are standard C programs and use `gcc`. +- Several level-2 directories include a `Makefile` with standard targets: + - `make` / `make all` — compile the program + - `make clean` — remove object files + - `make fclean` — remove object files and executable + - `make re` — rebuild from scratch + - `make test` — run example tests (where provided) + +Examples (run from repository root): + +Change into an exercise directory and build: + + cd level-2/n_queens + make + +For `tsp` the Makefile links against libm, so the `-lm` flag is used automatically. + +## Running the exercises (summary) + +Below are concise descriptions and usage examples for each exercise. For full +details refer to the exercise `subject.txt` or the directory README. + +- broken_gnl (level-1/broken_gnl) + - Purpose: an implementation of a `get_next_line`-like function. The + directory includes a subject and a sample (intentionally flawed) solution. + +- filter (level-1/filter) + - Purpose: read stdin and replace every occurrence of the given pattern with + a sequence of asterisks of equal length. + - Usage: `./filter ` + - Example: `echo "abcabc" | ./filter abc` outputs `*** ***` (without spaces) + +- scanf (level-1/scanf) + - Purpose: partial reimplementation of `scanf` supporting `%s`, `%d` and `%c`. + - The directory contains a working `ft_scanf.c` and helper subject file. + +- n_queens (level-2/n_queens) + - Purpose: print all valid placements of N queens on an N×N board. + - Usage: `./n_queens N` + - Output: lines with N integers representing row positions for each column. + +- permutations (level-2/permutations) + - Purpose: print all permutations of the input string in alphabetical order. + - Usage: `./permutations ` + +- powerset (level-2/powerset) + - Purpose: list all subsets of the provided set whose elements sum to the + target integer (first argument). + - Usage: `./powerset ` + +- tsp (level-2/tsp) + - Purpose: brute-force shortest closed path visiting all given points (n ≤ 11). + - Usage: `./tsp < input.txt` where input has lines like `x, y` (floating points). + - Output: a single floating number formatted to two decimals (e.g. `8.00`). + +- rip (level-2/rip) + - Purpose: remove the minimum number of parentheses (replace by spaces) to + make input strings balanced and print all distinct minimal solutions. + - Usage: `./rip "( )..."` + +## Testing notes + +- Many directories include example `Makefile` targets named `test` that run the + compiled binary with sample inputs from the exercise statements. Use those + targets to verify behavior quickly. +- For exercises that read from stdin, you can pipe data via `echo` or use file + redirection: `./tsp < level-2/tsp/square.txt`. + +## Quality and limitations + +- These are educational implementations. Some directories include intentionally + broken code (for debugging practice) or partial templates intended to be + completed. +- The programs follow the constraints given in each subject (allowed + functions, expected behavior on edge cases, and error handling). + +## Contributing and contact + +This repository is a personal collection of exercise solutions. Contributions are +welcome as long as they follow the repository license and respect the original +exercise authorship. For questions, corrections or requests regarding licensing +or removal of subject material, contact the repository owner (see `LICENSE`). + +--- + +Last updated: October 2025 diff --git a/level-1/broken_gnl/broken_gnl.c b/level-1/broken_gnl/broken_gnl.c new file mode 100644 index 0000000..f730541 --- /dev/null +++ b/level-1/broken_gnl/broken_gnl.c @@ -0,0 +1,97 @@ +#include "broken_gnl.h" + +#include +#include +#include + + +char *ft_strchr(char *s, int c) +{ + int i = 0; + while (s[i] && s[i] != c) + i++; + if (s[i] == c) + return (s + i); + else + return (NULL); +} + +void *ft_memcpy(void *dest, const void *src, size_t n) +{ + size_t i = 0; + while (i < n) + { + ((char *)dest)[i] = ((char *)src)[i]; + i++; + } + return (dest); + +} + +size_t ft_strlen(char *s) +{ + if (!s) + return 0; + size_t ret = 0; + while (*s) + { + s++; + ret++; + } + return (ret); +} + +int str_append_mem(char **s1, char *s2, size_t size2) +{ + size_t size1 = ft_strlen(*s1); + char *tmp = malloc(size2 + size1 + 1); + if (!tmp) + return (0); + if (*s1) + ft_memcpy(tmp, *s1, size1); + ft_memcpy(tmp + size1, s2, size2); + tmp [size1 + size2] = 0; + free(*s1); + *s1 = tmp; + return (1); +} + +int str_append_str(char **s1, char *s2) +{ + return (str_append_mem(s1, s2, ft_strlen(s2))); +} + +void *ft_memmove(void *dest, const void *src, size_t n) +{ + if (dest == src) + return (dest); + return (ft_memcpy(dest, src, n)); +} + +char *get_next_line(int fd) +{ + static char b[BUFFER_SIZE + 1] = ""; + char *ret = NULL; + + char *tmp = ft_strchr(b, '\n'); + while (!tmp) + { + if (!str_append_str(&ret, b)) + return (NULL); + int read_ret = read(fd, b, BUFFER_SIZE); + if (read_ret == -1 || read_ret == 0) + { + free(ret); + return (NULL); + } + b[read_ret] = 0; + tmp = ft_strchr(b, '\n'); + } + if (!str_append_mem(&ret, b, tmp - b + 1)) + { + free(ret); + return (NULL); + } + ft_memmove(b, tmp + 1, ft_strlen(tmp + 1) + 1); + return (ret); +} diff --git a/level-1/filter/filter.c b/level-1/filter/filter.c index e51a50f..95e005c 100644 --- a/level-1/filter/filter.c +++ b/level-1/filter/filter.c @@ -1,24 +1,4 @@ -/* - * EJERCICIO: FILTER - * - * DESCRIPCIÓN: - * Leer de stdin y escribir a stdout, pero reemplazando todas las ocurrencias - * de una cadena dada por asteriscos (*) de la misma longitud. - * - * CONCEPTOS CLAVE: - * 1. LECTURA DINÁMICA: read() con buffer variable - * 2. BÚSQUEDA DE PATRONES: strstr() o memmem() - * 3. GESTIÓN DE MEMORIA: realloc() para buffer dinámico - * 4. MANEJO DE ERRORES: perror() para errores de sistema - * - * ALGORITMO: - * 1. Leer todo el contenido de stdin en un buffer dinámico - * 2. Buscar todas las ocurrencias del patrón - * 3. Reemplazar cada ocurrencia con asteriscos - * 4. Escribir el resultado a stdout - */ - -#define _GNU_SOURCE // Para memmem() +#define _GNU_SOURCE #ifndef BUFFER_SIZE # define BUFFER_SIZE 42 #endif @@ -29,42 +9,30 @@ #include #include -// Función para encontrar y reemplazar todas las ocurrencias void ft_filter(char *buffer, const char *target) { int i = 0; int target_len = strlen(target); int j, k; - /* - * ALGORITMO DE BÚSQUEDA Y REEMPLAZO: - * - Recorrer el buffer carácter por carácter - * - En cada posición, verificar si coincide con el patrón - * - Si coincide, escribir asteriscos y saltar la longitud del patrón - * - Si no coincide, escribir el carácter original - */ - while (buffer[i]) { j = 0; - // Verificar si hay coincidencia desde la posición actual while (target[j] && (buffer[i + j] == target[j])) j++; - if (j == target_len) // Coincidencia completa encontrada + if (j == target_len) { - // Escribir asteriscos en lugar del patrón k = 0; while (k < target_len) { write(1, "*", 1); k++; } - i += target_len; // Saltar el patrón completo + i += target_len; } else { - // No hay coincidencia, escribir carácter original write(1, &buffer[i], 1); i++; } @@ -73,30 +41,18 @@ void ft_filter(char *buffer, const char *target) int main(int argc, char **argv) { - /* - * VALIDACIÓN DE ARGUMENTOS: - * - Debe haber exactamente 1 argumento - * - El argumento no puede estar vacío - */ + if (argc != 2 || argv[1][0] == '\0') return 1; - /* - * LECTURA DINÁMICA DE STDIN: - * - Usar buffer temporal para leer chunks - * - Usar realloc() para expandir el buffer principal - * - Mantener seguimiento del total leído - */ char temp[BUFFER_SIZE]; char *result = NULL; char *buffer; int total_read = 0; ssize_t bytes; - // Leer de stdin hasta EOF while ((bytes = read(0, temp, BUFFER_SIZE)) > 0) { - // Expandir el buffer principal para acomodar los nuevos datos buffer = realloc(result, total_read + bytes + 1); if (!buffer) { @@ -107,13 +63,11 @@ int main(int argc, char **argv) result = buffer; - // Copiar los nuevos datos al buffer principal memmove(result + total_read, temp, bytes); total_read += bytes; - result[total_read] = '\0'; // Asegurar terminación + result[total_read] = '\0'; } - // Verificar errores de lectura if (bytes < 0) { perror("read"); @@ -121,38 +75,10 @@ int main(int argc, char **argv) return 1; } - // Si no se leyó nada, salir sin error if (!result) return 0; - // Procesar el buffer y aplicar el filtro ft_filter(result, argv[1]); - - // Liberar memoria free(result); return 0; } - -/* - * PUNTOS CLAVE PARA EL EXAMEN: - * - * 1. GESTIÓN DE MEMORIA: - * - Siempre verificar el retorno de malloc/realloc - * - Liberar memoria en caso de error - * - Usar memmove() en lugar de memcpy() para solapamientos - * - * 2. MANEJO DE ERRORES: - * - Usar perror() para errores de sistema - * - Retornar códigos de error apropiados - * - Validar argumentos antes de usarlos - * - * 3. LECTURA DINÁMICA: - * - El buffer puede llenarse con cualquier cantidad de datos - * - Usar realloc() para expandir según sea necesario - * - Mantener un terminador nulo válido - * - * 4. ALGORITMO EFICIENTE: - * - Búsqueda simple carácter por carácter - * - Evitar usar funciones no permitidas - * - Escribir directamente a stdout sin almacenar el resultado - */ \ No newline at end of file diff --git a/level-1/scanf/ft_scanf b/level-1/scanf/ft_scanf deleted file mode 100644 index f526d9d..0000000 --- a/level-1/scanf/ft_scanf +++ /dev/null @@ -1,323 +0,0 @@ -/* - * EJERCICIO: FT_SCANF - * - * DESCRIPCIÓN: - * Implementar una versión simplificada de scanf que maneje solo %s, %d y %c. - * - * CONCEPTOS CLAVE: - * 1. ARGUMENTOS VARIABLES: va_list, va_start, va_arg, va_end - * 2. PARSING DE FORMATO: Analizar string de formato carácter por carácter - * 3. LECTURA DE ARCHIVO: fgetc(), ungetc() para control de flujo - * 4. CONVERSIONES: Convertir strings a números, manejar espacios en blanco - * - * FORMATO SOPORTADO: - * - %s: string (hasta el primer espacio en blanco) - * - %d: entero decimal (con signo opcional) - * - %c: un solo carácter - */ - -#include -#include -#include - -// Función para saltar espacios en blanco en el stream -int match_space(FILE *f) -{ - /* - * MANEJO DE ESPACIOS EN BLANCO: - * - Leer caracteres mientras sean espacios - * - Devolver el primer carácter no-espacio al stream - * - Retornar -1 en caso de error - */ - int ch = fgetc(f); - if (ch == EOF && ferror(f)) - return -1; - - while (ch != EOF) - { - if (!isspace(ch)) - { - ungetc(ch, f); // Devolver carácter no-espacio - break; - } - ch = fgetc(f); - } - - if (ferror(f)) - return -1; - return 1; -} - -// Función para verificar un carácter específico -int match_char(FILE *f, char c) -{ - /* - * COINCIDENCIA DE CARACTERES LITERALES: - * - Leer un carácter del stream - * - Verificar si coincide con el esperado - * - Devolver al stream si no coincide - */ - int ch = fgetc(f); - if (ch == c) - return 1; - if (ch != EOF) - ungetc(ch, f); - return -1; -} - -// Función para leer un carácter (%c) -int scan_char(FILE *f, va_list ap) -{ - /* - * CONVERSIÓN %c: - * - Leer exactamente un carácter - * - No saltar espacios en blanco - * - Almacenar en el puntero proporcionado - */ - int ch = fgetc(f); - char *cp = va_arg(ap, char *); - - if (ch == EOF) - return -1; - - *cp = (char)ch; - return 1; -} - -// Función para leer un entero (%d) -int scan_int(FILE *f, va_list ap) -{ - /* - * CONVERSIÓN %d: - * - Saltar espacios en blanco iniciales - * - Leer signo opcional (+/-) - * - Leer dígitos y construir el número - * - Devolver último carácter no-dígito al stream - */ - int sign = 1; - int value = 0; - int ch = fgetc(f); - int *ip = va_arg(ap, int *); - int count = 0; - - if (ch == EOF) - return -1; - - // Saltar espacios en blanco - while (isspace(ch)) - ch = fgetc(f); - - // Manejar signo - if (ch == '-') - { - sign = -1; - ch = fgetc(f); - } - else if (ch == '+') - { - ch = fgetc(f); - } - - // Verificar que el primer carácter sea un dígito - if (!isdigit(ch)) - { - ungetc(ch, f); - return -1; - } - - // Leer dígitos y construir el número - while (isdigit(ch)) - { - value = value * 10 + (ch - '0'); - count++; - ch = fgetc(f); - } - - // Devolver último carácter no-dígito - if (ch != EOF) - ungetc(ch, f); - - if (count == 0) - return -1; - - *ip = value * sign; - return 1; -} - -// Función para leer un string (%s) -int scan_string(FILE *f, va_list ap) -{ - /* - * CONVERSIÓN %s: - * - Saltar espacios en blanco iniciales - * - Leer caracteres hasta encontrar espacio en blanco - * - Terminar el string con '\0' - * - Devolver último carácter de espacio al stream - */ - int ch = fgetc(f); - char *sp = va_arg(ap, char *); - int i = 0; - - // Saltar espacios en blanco iniciales - while (ch != EOF && isspace(ch)) - ch = fgetc(f); - - if (ch == EOF) - return -1; - - // Leer caracteres hasta espacio en blanco - do - { - sp[i] = ch; - i++; - ch = fgetc(f); - } while (ch != EOF && !isspace(ch)); - - sp[i] = '\0'; // Terminar string - - // Devolver carácter de espacio al stream - if (ch != EOF) - ungetc(ch, f); - - if (i == 0) - return -1; - return 1; -} - -// Función para manejar conversiones de formato -int match_conv(FILE *f, const char **format, va_list ap) -{ - /* - * DISPATCHER DE CONVERSIONES: - * - Analizar el carácter de conversión - * - Llamar a la función apropiada - * - Manejar espacios para %d y %s automáticamente - */ - switch (**format) - { - case 'c': - return scan_char(f, ap); - case 'd': - match_space(f); // %d salta espacios automáticamente - return scan_int(f, ap); - case 's': - match_space(f); // %s salta espacios automáticamente - return scan_string(f, ap); - case '\0': - return -1; - default: - return -1; // Conversión no soportada - } -} - -// Función principal de scanf -int ft_vfscanf(FILE *f, const char *format, va_list ap) -{ - /* - * LÓGICA PRINCIPAL DE SCANF: - * - Analizar string de formato carácter por carácter - * - Manejar caracteres literales y conversiones (%) - * - Contar conversiones exitosas - * - Parar en el primer error - */ - int nconv = 0; // Número de conversiones exitosas - - // Verificar que hay datos disponibles - int c = fgetc(f); - if (c == EOF) - return EOF; - ungetc(c, f); - - while (*format) - { - if (*format == '%') - { - // Conversión encontrada - format++; - if (match_conv(f, &format, ap) != 1) - break; // Error en conversión - else - nconv++; // Conversión exitosa - } - else if (isspace(*format)) - { - // Espacio en blanco en formato: saltar espacios en input - if (match_space(f) == -1) - break; - } - else - { - // Carácter literal: debe coincidir exactamente - if (match_char(f, *format) != 1) - break; - } - format++; - } - - // Verificar errores de archivo - if (ferror(f)) - return EOF; - - return nconv; // Retornar número de conversiones exitosas -} - -// Función wrapper para scanf estándar -int ft_scanf(const char *format, ...) -{ - /* - * WRAPPER PARA ARGUMENTOS VARIABLES: - * - Inicializar va_list - * - Llamar a la función principal con stdin - * - Limpiar va_list - */ - va_list ap; - - va_start(ap, format); - int ret = ft_vfscanf(stdin, format, ap); - va_end(ap); - - return ret; -} - -/* - * EJEMPLO DE USO: - * - * int main(void) - * { - * int x; - * char str[100]; - * char c; - * - * // Leer: número, espacio, string, espacio, carácter - * int converted = ft_scanf("%d %s %c", &x, str, &c); - * - * printf("Convertidos: %d\n", converted); - * printf("Número: %d, String: %s, Carácter: %c\n", x, str, c); - * - * return 0; - * } - */ - -/* - * PUNTOS CLAVE PARA EL EXAMEN: - * - * 1. ARGUMENTOS VARIABLES: - * - va_start(ap, last_param) para inicializar - * - va_arg(ap, type) para obtener siguiente argumento - * - va_end(ap) para limpiar - * - * 2. CONTROL DE FLUJO DE ARCHIVO: - * - fgetc() para leer carácter - * - ungetc() para devolver carácter al stream - * - ferror() para verificar errores - * - * 3. MANEJO DE ESPACIOS: - * - %c NO salta espacios en blanco - * - %d y %s SÍ saltan espacios en blanco - * - Espacios en formato coinciden con cualquier whitespace - * - * 4. VALOR DE RETORNO: - * - Número de conversiones exitosas - * - EOF si error de archivo o EOF antes de conversiones - * - Parar en primera conversión fallida - */ \ No newline at end of file diff --git a/level-1/scanf/ft_scanf_clean.c b/level-1/scanf/ft_scanf.c similarity index 72% rename from level-1/scanf/ft_scanf_clean.c rename to level-1/scanf/ft_scanf.c index 2fba64a..525859c 100644 --- a/level-1/scanf/ft_scanf_clean.c +++ b/level-1/scanf/ft_scanf.c @@ -2,67 +2,47 @@ #include #include -/* - * Consome todos os caracteres de espaço em branco consecutivos do stream. - * Retorna 1 em sucesso, -1 em caso de erro de leitura. - */ + int match_space(FILE *f) { int ch; - // Lê caracteres enquanto não for o fim do ficheiro e forem espaços while ((ch = fgetc(f)) != EOF && isspace(ch)) - { - // O corpo do loop está vazio de propósito, apenas consome os caracteres - } + {} - // Se o loop parou por um caractere que não é espaço, coloca-o de volta if (ch != EOF) ungetc(ch, f); - // Verifica se ocorreu um erro de leitura durante as operações if (ferror(f)) return -1; return 1; } -/* - * Tenta corresponder a um caractere específico 'c'. - * Já era simples e eficiente, não necessitou de alterações. - */ int match_char(FILE *f, char c) { int ch = fgetc(f); if (ch == c) - return 1; // Sucesso, caractere corresponde + return 1; - // Se não correspondeu, e não é o fim do ficheiro, devolve o caractere if (ch != EOF) ungetc(ch, f); - return -1; // Falha na correspondência + return -1; } -/* - * Lê um único caractere do stream e armazena-o. - * Já era simples e direto, não necessitou de alterações. - */ int scan_char(FILE *f, va_list ap) { int ch = fgetc(f); char *cp = va_arg(ap, char *); if (ch == EOF) - return -1; // Falha se chegar ao fim do ficheiro + return -1; *cp = (char)ch; return 1; } -/* - * Lê um inteiro do stream, ignorando espaços iniciais e tratando o sinal. - */ int scan_int(FILE *f, va_list ap) { int *ip = va_arg(ap, int *); diff --git a/level-2/n_queens/README.md b/level-2/n_queens/README.md index 2d5a27b..7e42261 100644 --- a/level-2/n_queens/README.md +++ b/level-2/n_queens/README.md @@ -1,19 +1,19 @@ -# N-Queens Problem - Simple Explanation 👑 +# N-Queens -## What is the N-Queens Problem? 🤔 +## What is the N-Queens Problem? Imagine you have a chessboard and some queens (the most powerful pieces in chess). The N-Queens problem is like a puzzle where you need to place N queens on an N×N chessboard so that **none of them can attack each other**. -### What's a Queen in Chess? ♛ +### What's a Queen in Chess? A queen is the strongest piece in chess. She can move: -- **Horizontally** (left and right) ←→ -- **Vertically** (up and down) ↕️ -- **Diagonally** (in any diagonal direction) ↗️↖️↙️↘️ +- **Horizontally** (left and right) +- **Vertically** (up and down) +- **Diagonally** (in any diagonal direction) She can move as many squares as she wants in these directions! -## The Challenge 🎯 +## The Challenge Let's say we want to solve the **4-Queens problem** (N=4): - We have a 4×4 chessboard (16 squares total) @@ -45,208 +45,4 @@ Q . . . ← Queen attacks diagonally . . . X ``` -So we can't put any other queen in any of those X positions! - -## How Our Program Works 🖥️ - -### Step 1: Understanding the Input -When you run our program like this: -```bash -./n_queens 4 -``` - -The number `4` means "solve the 4-Queens problem" (4×4 board with 4 queens). - -### Step 2: How We Represent the Solution - -Instead of drawing the whole board, we use a clever trick! Since we know: -- There must be exactly ONE queen in each row -- There must be exactly ONE queen in each column - -We can represent a solution as a list of numbers: -``` -1 3 0 2 -``` - -This means: -- **Row 0**: Queen is in column 1 -- **Row 1**: Queen is in column 3 -- **Row 2**: Queen is in column 0 -- **Row 3**: Queen is in column 2 - -### Step 3: Visualizing the Solution - -If we draw this on a 4×4 board: - -``` -. Q . . ← Row 0, Column 1 -. . . Q ← Row 1, Column 3 -Q . . . ← Row 2, Column 0 -. . Q . ← Row 3, Column 2 -``` - -Let's check: Can any queen attack another? -- Queen at (0,1): Can't attack any other queen ✅ -- Queen at (1,3): Can't attack any other queen ✅ -- Queen at (2,0): Can't attack any other queen ✅ -- Queen at (3,2): Can't attack any other queen ✅ - -Perfect! This is a valid solution! - -## How Our Code Works 🔧 - -### The Main Function (`main`) - -```c -int main(int argc, char **argv) -{ - int n; - int *queens; - - if (argc != 2) // Check if user gave us exactly one number - return (1); - n = atoi(argv[1]); // Convert the text "4" to number 4 - if (n <= 0) // Make sure the number is positive - return (1); - queens = (int *)malloc(sizeof(int) * n); // Create space to store queen positions - if (!queens) // Check if we got the memory - return (1); - solve_nqueens(queens, 0, n); // Start solving from row 0 - free(queens); // Clean up memory - return (0); -} -``` - -**What this does in simple terms:** -1. "Did the user give me a number?" -2. "Is it a good number (positive)?" -3. "Let me create space to remember where I put the queens" -4. "Now let me solve the puzzle!" -5. "Clean up when I'm done" - -### The Solver Function (`solve_nqueens`) - -This is the "brain" of our program. It uses a technique called **backtracking**: - -```c -void solve_nqueens(int *queens, int row, int n) -{ - int col; - - if (row == n) // Did I place all queens? - { - print_solution(queens, n); // Yes! Print this solution - return ; - } - col = 0; - while (col < n) // Try each column in this row - { - if (is_safe(queens, row, col, n)) // Can I put a queen here? - { - queens[row] = col; // Yes! Put the queen here - solve_nqueens(queens, row + 1, n); // Try the next row - } - col++; - } -} -``` - -**Think of it like this:** -1. "Am I done placing all queens?" → If yes, print the solution! -2. "If not, let me try putting a queen in each column of this row" -3. "For each column, ask: Is it safe to put a queen here?" -4. "If safe, put the queen there and try to solve the next row" - -### The Safety Check (`is_safe`) - -This function checks if a queen position is safe: - -```c -int is_safe(int *queens, int row, int col, int n) -{ - int i; - - i = 0; - while (i < row) // Check all previously placed queens - { - if (queens[i] == col) // Same column? - return (0); // Not safe! - if (queens[i] - i == col - row) // Same diagonal (\)? - return (0); // Not safe! - if (queens[i] + i == col + row) // Same diagonal (/)? - return (0); // Not safe! - i++; - } - return (1); // Safe to place queen here! -} -``` - -**The three checks:** - -1. **Same column**: `queens[i] == col` - - "Is there already a queen in this column?" - -2. **Diagonal 1**: `queens[i] - i == col - row` - - "Is there a queen on the same diagonal going from top-left to bottom-right?" - -3. **Diagonal 2**: `queens[i] + i == col + row` - - "Is there a queen on the same diagonal going from top-right to bottom-left?" - -## Example Run 🏃‍♂️ - -Let's trace through what happens when we run `./n_queens 4`: - -1. **Start with row 0**: Try putting a queen in each column - - Column 0: Check safety → Safe! Put queen at (0,0) - - Move to row 1 - -2. **Row 1**: Try each column - - Column 0: Not safe (same column as row 0) - - Column 1: Not safe (same column... wait, no queen there yet) - - Column 2: Check safety → Safe! Put queen at (1,2) - - Move to row 2 - -3. **Row 2**: Try each column - - Column 0: Not safe (diagonal conflict) - - Column 1: Not safe (diagonal conflict) - - Column 2: Not safe (same column as row 1) - - Column 3: Not safe (diagonal conflict) - - **No solution found!** Go back to row 1 - -4. **Back to row 1**: Try next column - - Column 3: Check safety → Safe! Put queen at (1,3) - - Move to row 2 - -5. Continue this process... - -Eventually, we find: `1 3 0 2` and `2 0 3 1` - -## Results for Different N Values 📊 - -- **N=1**: `0` (1 solution - just put the queen anywhere) -- **N=2**: No output (impossible to solve) -- **N=3**: No output (impossible to solve) -- **N=4**: `1 3 0 2` and `2 0 3 1` (2 solutions) -- **N=8**: 92 solutions! - -## Fun Facts! 🎉 - -1. **Why no solution for N=2 and N=3?** - - For N=2: You need 2 queens on a 2×2 board, but they'll always attack each other! - - For N=3: Same problem - not enough space! - -2. **The 8-Queens problem** (standard chessboard) has exactly **92 solutions**! - -3. **This is a classic computer science problem** that teaches us about: - - **Recursion** (functions calling themselves) - - **Backtracking** (trying something, and if it doesn't work, going back and trying something else) - - **Problem solving** (breaking a big problem into smaller pieces) - -## How to Use the Program 💻 - -1. **Compile**: `make` -2. **Run**: `./n_queens 4` -3. **Test different values**: `make test` -4. **Clean up**: `make clean` - -Try it with different numbers and see what happens! 🚀 \ No newline at end of file +So we can't put any other queen in any of those X positions! \ No newline at end of file diff --git a/level-2/permutations/README.md b/level-2/permutations/README.md index 4621e13..a20a0bd 100644 --- a/level-2/permutations/README.md +++ b/level-2/permutations/README.md @@ -1,6 +1,6 @@ -# Permutations Exercise - Simple Explanation 🔄 +# Permutations -## What is a Permutation? 🤔 +## What is a Permutation? Imagine you have some letters, like the letters in your name. A **permutation** is just a fancy word for "all the different ways you can arrange those letters." @@ -10,14 +10,14 @@ Think of it like this: It's like having alphabet blocks and seeing how many different words you can spell by changing the order! -## The Challenge 🎯 +## The Challenge Your mission is to write a program that: 1. Takes a word (like "abc") 2. Shows ALL possible ways to rearrange the letters 3. Shows them in **alphabetical order** (like in a dictionary) -## Real Examples 📝 +## Real Examples Let's see what our program does: @@ -54,147 +54,3 @@ bca cab cba ``` - -*Why?* Think of it step by step: -- Start with 'a': we can make **abc** and **acb** -- Start with 'b': we can make **bac** and **bca** -- Start with 'c': we can make **cab** and **cba** - -## How Our Code Works 🛠️ - -### Step 1: Getting Ready -```c -// We take the word you give us -char *str = argv[1]; // This is your word like "abc" - -// We make a copy so we don't mess up the original -copy = malloc(sizeof(char) * (len + 1)); -``` - -### Step 2: Sorting First (The Secret Sauce! ✨) -```c -ft_sort_string(copy); // This puts letters in order: "cba" becomes "abc" -``` - -**Why do we sort first?** -- If someone gives us "cba", we sort it to "abc" first -- This way, we always start with letters in alphabetical order -- Then when we make all arrangements, they come out in the right order! - -### Step 3: The Magic Swapping Function -```c -void ft_swap(char *a, char *b) -{ - char temp = *a; // Remember the first letter - *a = *b; // Put the second letter in first position - *b = temp; // Put the first letter in second position -} -``` - -**What's swapping?** It's like switching two cards in your hand: -- You have cards **A** and **B** -- After swapping, you have **B** and **A** - -### Step 4: The Next Permutation Magic 🪄 -```c -int next_permutation(char *str, int len) -{ - int i = len - 2; - while (i >= 0 && str[i] >= str[i + 1]) // Find rightmost character smaller than next - i--; - if (i < 0) - return (0); // No more permutations - - int j = len - 1; - while (str[j] <= str[i]) // Find rightmost character greater than str[i] - j--; - ft_swap(&str[i], &str[j]); // Swap them - ft_reverse(str, i + 1, len - 1); // Reverse the suffix - return (1); // Successfully generated next permutation -} -``` - -**How does this work?** Think of it like counting in a special way: -1. **Find** the rightmost place where we can "increment" (make it bigger) -2. **Swap** with the next bigger character available -3. **Reverse** everything after that position to get the smallest arrangement -4. **Repeat** until no more permutations exist - -It's like a smart odometer that counts through all possible letter arrangements in perfect alphabetical order! - -### Step 5: The Main Loop 🔄 -```c -ft_sort_string(copy); // Start with alphabetically first arrangement -puts(copy); // Print the first permutation -while (next_permutation(copy, len)) // While there are more permutations - puts(copy); // Print each one -``` - -**How does this work?** -1. **Start** with the smallest (first) arrangement: "abc" -2. **Print** it -3. **Generate** the next alphabetical arrangement using `next_permutation` -4. **Print** it -5. **Repeat** until `next_permutation` says "no more!" - -It's like flipping through a dictionary - each page (permutation) comes in perfect alphabetical order! - -## Why This Approach is Cool 😎 - -### The Step-by-Step Process 📋 -When we have "abc", our program works like this: - -``` -1. Start: abc (sorted, print it!) -2. Next: acb (swap 'b' and 'c', print it!) -3. Next: bac (find next in alphabetical order, print it!) -4. Next: bca (continue the pattern, print it!) -5. Next: cab (keep going, print it!) -6. Next: cba (last one, print it!) -7. Done: No more permutations possible -``` - -**The Magic:** Each step finds the next arrangement that would come after the current one in a dictionary! It's like having a super-smart assistant who knows exactly what comes next alphabetically. - -### Memory Management 🧠 -```c -copy = malloc(sizeof(char) * (len + 1)); // Ask for memory -// ... do our work ... -free(copy); // Give memory back when done -``` - -We're polite programmers - we clean up after ourselves! - -### Error Handling 🛡️ -```c -if (argc != 2) // Did you give us exactly one word? - return (1); // If not, we quit - -if (!copy) // Did we get the memory we asked for? - return (1); // If not, we quit safely -``` - -We check if things went wrong and handle it gracefully. - -## Fun Facts! 🎉 - -- With 1 letter: **1** permutation -- With 2 letters: **2** permutations -- With 3 letters: **6** permutations -- With 4 letters: **24** permutations -- With 5 letters: **120** permutations - -**Pattern?** For n letters, you get n × (n-1) × (n-2) × ... × 1 permutations! - -## Try It Yourself! 🚀 - -1. Compile the program: `gcc -Wall -Wextra -Werror permutations.c -o permutations` -2. Try it with your name: `./permutations john` -3. Try it with short words: `./permutations cat` -4. See how the results are always in alphabetical order! - -Remember: The computer is doing exactly what we told it to do, step by step, just like following a recipe! 👨‍🍳 - ---- - -*Made with ❤️ for curious minds who want to understand how permutations work!* \ No newline at end of file diff --git a/level-2/powerset/README.md b/level-2/powerset/README.md index ef51e34..09dfda5 100644 --- a/level-2/powerset/README.md +++ b/level-2/powerset/README.md @@ -1,4 +1,4 @@ -# Powerset Exercise - Simple Explanation 🧮 +# Powerset ## What is this exercise about? @@ -6,16 +6,16 @@ Imagine you have a bag of numbered balls, and you want to find all the different This is exactly what the **powerset** exercise does! -## Real-world example 🎯 +## Real-world example Let's say you have these numbered balls: **1, 2, 3, 4, 5** And you want to find all the ways to pick balls that add up to **5**. Here are all the possible ways: -- Pick just ball **5** → 5 = 5 ✅ -- Pick balls **1** and **4** → 1 + 4 = 5 ✅ -- Pick balls **2** and **3** → 2 + 3 = 5 ✅ +- Pick just ball **5** → 5 = 5 +- Pick balls **1** and **4** → 1 + 4 = 5 +- Pick balls **2** and **3** → 2 + 3 = 5 So the answer would be: ``` @@ -23,153 +23,3 @@ So the answer would be: 1 4 2 3 ``` - -## How does our program work? 🤖 - -### Step 1: Understanding the input -When you run the program like this: -```bash -./powerset 5 1 2 3 4 5 -``` - -- **5** is our target number (what we want the balls to add up to) -- **1 2 3 4 5** are our numbered balls - -### Step 2: The magic behind the scenes - -Our program is like a smart robot that tries **every possible combination**: - -1. **Try no balls at all** → sum = 0 (not 5, so skip) -2. **Try just ball 1** → sum = 1 (not 5, so skip) -3. **Try just ball 2** → sum = 2 (not 5, so skip) -4. **Try just ball 3** → sum = 3 (not 5, so skip) -5. **Try just ball 4** → sum = 4 (not 5, so skip) -6. **Try just ball 5** → sum = 5 ✅ **FOUND ONE!** -7. **Try balls 1 and 2** → sum = 3 (not 5, so skip) -8. **Try balls 1 and 3** → sum = 4 (not 5, so skip) -9. **Try balls 1 and 4** → sum = 5 ✅ **FOUND ANOTHER!** -10. **Try balls 2 and 3** → sum = 5 ✅ **FOUND ANOTHER!** - -...and so on until it tries every possible combination! - -## Code explanation 📝 - -### The main parts of our code: - -#### 1. Reading the input (`main` function) -```c -target = atoi(argv[1]); // Get the target number (5 in our example) -set[i] = atoi(argv[i + 2]); // Get each ball number (1, 2, 3, 4, 5) -``` - -#### 2. The smart robot (`find_subsets` function) -This function is like our robot that tries every combination: - -```c -// For each ball, we have 2 choices: -find_subsets(..., index + 1); // Don't pick this ball -subset[subset_size] = set[index]; // Pick this ball -find_subsets(..., subset_size + 1, ...); // Continue with this ball included -``` - -#### 3. Checking if we found a winner (`print_subset`) -```c -if (current_sum == target) // If the sum equals our target - print_subset(subset, subset_size); // Show this combination! -``` - -## More examples to understand better 🎲 - -### Example 1: Finding combinations that sum to 3 -```bash -./powerset 3 1 0 2 4 5 3 -``` - -**What the robot finds:** -- Ball **3** alone → 3 ✅ -- Balls **0** and **3** → 0 + 3 = 3 ✅ -- Balls **1** and **2** → 1 + 2 = 3 ✅ -- Balls **1**, **0**, and **2** → 1 + 0 + 2 = 3 ✅ - -**Output:** -``` -3 -0 3 -1 2 -1 0 2 -``` - -### Example 2: Finding the empty combination -```bash -./powerset 0 1 -1 -``` - -**What the robot finds:** -- No balls picked → sum = 0 ✅ (shows as empty line) -- Balls **1** and **-1** → 1 + (-1) = 0 ✅ - -**Output:** -``` - -1 -1 -``` -(Notice the empty line at the top!) - -### Example 3: When nothing works -```bash -./powerset 7 3 8 2 -``` - -**What the robot finds:** -- No combination of 3, 8, and 2 can make 7 -- So nothing gets printed (empty output) - -## Important rules 📏 - -1. **Order matters**: We always keep balls in the same order as given - - ✅ Correct: `1 4` (1 comes before 4 in input) - - ❌ Wrong: `4 1` (this changes the order) - -2. **No duplicates**: Each ball can only be used once per combination - -3. **Empty combination counts**: If target is 0, picking no balls is valid! - -## How to use the program 🚀 - -1. **Compile it:** - ```bash - make - ``` - -2. **Run tests:** - ```bash - make test - ``` - -3. **Try your own examples:** - ```bash - ./powerset [target_number] [ball1] [ball2] [ball3] ... - ``` - -4. **Clean up:** - ```bash - make clean # Remove temporary files - make fclean # Remove everything - ``` - -## Fun challenge! 🎮 - -Try to predict what this will output before running it: -```bash -./powerset 6 1 2 3 4 -``` - -**Think about it:** -- Which combinations of 1, 2, 3, 4 add up to 6? -- Remember: order matters and each number can only be used once! - -**Answer:** `2 4` and `1 2 3` (because 2+4=6 and 1+2+3=6) - ---- - -*Now you understand how the powerset exercise works! It's like having a super-smart robot that can instantly try every possible combination of numbers to find the ones that add up to your target. Pretty cool, right?* 🤖✨ \ No newline at end of file diff --git a/level-2/rip/README.md b/level-2/rip/README.md index 84671e4..cad7e38 100644 --- a/level-2/rip/README.md +++ b/level-2/rip/README.md @@ -26,154 +26,8 @@ This means: 1. Open door 1: `(` 2. Open door 2: `(` 3. Close door 2: `)` -4. But door 1 is still open! 😱 +4. But door 1 is still open! To fix this, we need to either: - Remove one of the opening doors: ` ()` (remove first `(`) - Or remove one of the opening doors: `( )` (remove second `(`) - -Both solutions work! - -## How Our Program Works - -### Step 1: Count the Problems - -```c -// This function counts how many parentheses we need to remove -void calculate_removals(char *str, int *left_rem, int *right_rem) -{ - int left = 0; // Count of unmatched opening parentheses - int right = 0; // Count of unmatched closing parentheses - - // Go through each character - while (str[i]) - { - if (str[i] == '(') - left++; // Found an opening door - else if (str[i] == ')') - { - if (left > 0) - left--; // Found a closing door for an open one - else - right++; // Found a closing door with no matching open door - } - } -} -``` - -**Example with `"(()"`:** -- Start: `left = 0`, `right = 0` -- See `(`: `left = 1` (one unmatched opening) -- See `(`: `left = 2` (two unmatched openings) -- See `)`: `left = 1` (one opening got matched) -- End: `left = 1`, `right = 0` - -So we need to remove **1 opening parenthesis**. - -### Step 2: Try All Possible Solutions - -Think of it like trying different combinations: - -```c -// This function tries removing different parentheses to find all valid solutions -void solve(char *s, int pos, int left_rem, int right_rem, int open, char *path) -{ - // If we've looked at all characters - if (pos == len) - { - // Check if we removed exactly what we needed and everything balances - if (left_rem == 0 && right_rem == 0 && open == 0) - add_result(results, path); // This is a valid solution! - return; - } - - // Try removing this parenthesis (replace with space) - if (s[pos] == '(' && left_rem > 0) - { - path[pos] = ' '; // Replace with space - solve(s, pos + 1, left_rem - 1, right_rem, open, path); - } - - // Try keeping this parenthesis - path[pos] = s[pos]; - if (s[pos] == '(') - solve(s, pos + 1, left_rem, right_rem, open + 1, path); - // ... and so on -} -``` - -### Step 3: Avoid Duplicates - -We use a list to store all unique solutions: - -```c -// This makes sure we don't print the same solution twice -void add_result(t_result **results, char *str) -{ - // Check if we already have this solution - current = *results; - while (current) - { - if (ft_strcmp(current->str, str) == 0) - return; // Already have it, don't add again - current = current->next; - } - - // It's new, so add it to our list - new->str = ft_strdup(str); - new->next = *results; - *results = new; -} -``` - -## Example Walkthrough - -Let's trace through `"(()"` step by step: - -### Initial Analysis: -- Need to remove 1 opening parenthesis (`left_rem = 1`) -- Need to remove 0 closing parentheses (`right_rem = 0`) - -### Trying Different Positions: - -**Position 0 (first `(`):** -- Try removing it: `" ()"` - - Check: removed 1 opening ✓, no unmatched doors ✓ - - **Valid solution!** ✅ - -**Position 1 (second `(`):** -- Try removing it: `"( )"` - - Check: removed 1 opening ✓, no unmatched doors ✓ - - **Valid solution!** ✅ - -**Position 2 (the `)`):** -- Can't remove it (we need to remove openings, not closings) - -### Final Output: -``` - () -( ) -``` - -## More Complex Example: `"()())()"` - -This string has: -- 3 opening doors: `(`, `(`, `(` -- 4 closing doors: `)`, `)`, `)`, `)` - -So we have 1 extra closing door that needs to be removed. - -**All possible solutions:** -- Remove closing at position 2: `"()( )()"` -- Remove closing at position 3: `"()() ()"` -- Remove closing at position 4: `"( ())()"` - -## Why This Exercise is Useful - -This problem teaches us: -1. **Problem-solving**: Break down complex problems into smaller steps -2. **Recursion**: Try all possibilities systematically -3. **Data structures**: Use lists to store and manage results -4. **Optimization**: Find the minimum changes needed - -It's like being a **door inspector** - you need to make sure every building (string) has properly matched doors (parentheses) with the minimum number of changes! \ No newline at end of file diff --git a/level-2/tsp/README.md b/level-2/tsp/README.md new file mode 100644 index 0000000..177caa5 --- /dev/null +++ b/level-2/tsp/README.md @@ -0,0 +1,36 @@ +# Traveling Salesman Problem (TSP) + +## What is the Traveling Salesman Problem? + +Imagine you're a delivery person who needs to visit several houses in your neighborhood and then return home. You want to find the **shortest possible route** that visits every house exactly once and brings you back to where you started. + +That's exactly what the Traveling Salesman Problem is about! + +### Real-World Example +Let's say you need to deliver pizza to 4 houses: +- Your home: (0, 0) +- House A: (1, 0) +- House B: (1, 1) +- House C: (0, 1) + +``` +C ---- B +| | +| | +Home - A +``` + +You could go in many different orders: +- Home → A → B → C → Home +- Home → A → C → B → Home +- Home → B → A → C → Home +- ... and many more! + +But which path is the shortest? That's what our algorithm finds out! + +## How Our Algorithm Works 🔍 + +Our solution uses a "brute force" approach, which means: +**"Let's try EVERY possible path and pick the shortest one!"** + +It's like trying on every shirt in your closet to find the one that fits best - not the fastest way, but it guarantees you'll find the perfect answer! diff --git a/level-2/tsp/TSP_Algorithm_Explained.md b/level-2/tsp/TSP_Algorithm_Explained.md deleted file mode 100644 index 42531ed..0000000 --- a/level-2/tsp/TSP_Algorithm_Explained.md +++ /dev/null @@ -1,247 +0,0 @@ -# The Traveling Salesman Problem (TSP) - Simple Explanation - -## What is the Traveling Salesman Problem? 🗺️ - -Imagine you're a delivery person who needs to visit several houses in your neighborhood and then return home. You want to find the **shortest possible route** that visits every house exactly once and brings you back to where you started. - -That's exactly what the Traveling Salesman Problem is about! - -### Real-World Example -Let's say you need to deliver pizza to 4 houses: -- Your home: (0, 0) -- House A: (1, 0) -- House B: (1, 1) -- House C: (0, 1) - -``` -C ---- B -| | -| | -Home - A -``` - -You could go in many different orders: -- Home → A → B → C → Home -- Home → A → C → B → Home -- Home → B → A → C → Home -- ... and many more! - -But which path is the shortest? That's what our algorithm finds out! - -## How Our Algorithm Works 🔍 - -Our solution uses a "brute force" approach, which means: -**"Let's try EVERY possible path and pick the shortest one!"** - -It's like trying on every shirt in your closet to find the one that fits best - not the fastest way, but it guarantees you'll find the perfect answer! - -### Step 1: Calculate Distance Between Two Points - -First, we need to know how far apart two places are: - -```c -static float distance(t_city a, t_city b) -{ - float dx = b.x - a.x; // How far left/right? - float dy = b.y - a.y; // How far up/down? - return (sqrtf(dx * dx + dy * dy)); // Pythagorean theorem! -} -``` - -**What's happening here?** -- `dx` = horizontal distance (like counting steps left or right) -- `dy` = vertical distance (like counting steps up or down) -- `sqrtf(dx*dx + dy*dy)` = the straight-line distance (like a bird flying) - -**Example:** -From Home (0,0) to House A (1,0): -- dx = 1 - 0 = 1 -- dy = 0 - 0 = 0 -- distance = √(1² + 0²) = √1 = 1.0 - -### Step 2: Calculate Total Path Length - -Now we add up all the distances in a complete round trip: - -```c -static float path_length(t_city *cities, int *route, int n) -{ - float total = 0; - int i = 0; - - while (i < n) - { - // Add distance from current city to next city - total += distance(cities[route[i]], cities[route[(i + 1) % n]]); - i++; - } - return (total); -} -``` - -**What's that weird `% n` thing?** -It's like a clock! When you get to hour 12, the next hour is 1, not 13. -- When `i = 3` (last city) and `n = 4` (total cities) -- `(i + 1) % n = (3 + 1) % 4 = 4 % 4 = 0` -- So we go from the last city back to the first city (completing the circle!) - -### Step 3: Try All Possible Routes - -This is where the magic happens! We generate every possible order of visiting the cities: - -```c -static void solve(t_city *cities, int *route, int pos, int n, float *best) -{ - float current; - int i, temp; - - // If we've arranged all cities, check this route - if (pos == n) - { - current = path_length(cities, route, n); - if (current < *best) - *best = current; // Found a better route! - return; - } - - // Try putting each remaining city in the current position - i = pos; - while (i < n) - { - // Swap cities (like switching the order of your visits) - temp = route[pos]; - route[pos] = route[i]; - route[i] = temp; - - // Try all arrangements with this city in this position - solve(cities, route, pos + 1, n, best); - - // Swap back (undo the change) - temp = route[pos]; - route[pos] = route[i]; - route[i] = temp; - i++; - } -} -``` - -**Think of it like this:** -- Position 0: "Which city should I visit first?" -- Position 1: "Which city should I visit second?" -- Position 2: "Which city should I visit third?" -- And so on... - -For each position, we try putting each remaining city there and see what happens! - -## Complete Example Walkthrough 🚶‍♂️ - -Let's trace through a simple 3-city example: -- City 0: (0, 0) -- City 1: (1, 0) -- City 2: (0, 1) - -``` -2 -| -| -0 --- 1 -``` - -**All possible routes:** -1. 0 → 1 → 2 → 0: distance = 1 + √2 + 1 = 3.41 -2. 0 → 2 → 1 → 0: distance = 1 + √2 + 1 = 3.41 -3. 1 → 0 → 2 → 1: distance = 1 + 1 + √2 = 3.41 -4. 1 → 2 → 0 → 1: distance = √2 + 1 + 1 = 3.41 -5. 2 → 0 → 1 → 2: distance = 1 + 1 + √2 = 3.41 -6. 2 → 1 → 0 → 2: distance = √2 + 1 + 1 = 3.41 - -**Result:** All routes have the same length! (This makes sense - it's a triangle, so any direction around it is the same distance.) - -## The Main Program 🏠 - -```c -int main(void) -{ - t_city cities[11]; // Space for up to 11 cities - int route[11]; // The order we'll visit cities - int n = 0; // How many cities we actually have - float best = 999999.0f; // Start with a really big number - - // Read city coordinates from input - while (n < 11 && fscanf(stdin, "%f, %f", &cities[n].x, &cities[n].y) == 2) - { - route[n] = n; // Initially: visit cities in order 0,1,2,3... - n++; - } - - if (n < 2) - return (1); // Need at least 2 cities for a trip! - - solve(cities, route, 0, n, &best); // Find the best route - fprintf(stdout, "%.2f\n", best); // Print the shortest distance - return (0); -} -``` - -## Why This Works (But Is Slow) ⏰ - -**The Good:** -- ✅ **Always finds the perfect answer** - we check every possibility! -- ✅ **Simple to understand** - no complex tricks or shortcuts -- ✅ **Works for any arrangement of cities** - -**The Not-So-Good:** -- ❌ **Gets very slow with more cities** -- For 3 cities: 6 routes to check -- For 4 cities: 24 routes to check -- For 10 cities: 3,628,800 routes to check! 😱 - -**Why it gets so big:** -- 1st city: 10 choices -- 2nd city: 9 choices (can't repeat) -- 3rd city: 8 choices -- ... -- Total: 10 × 9 × 8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 3,628,800 - -This is called "factorial growth" - it explodes very quickly! - -## Key Programming Concepts Used 💻 - -### 1. **Recursion** -The `solve()` function calls itself! It's like Russian nesting dolls - each call handles one position, then asks a smaller version of itself to handle the rest. - -### 2. **Backtracking** -We try something (swap cities), explore all possibilities, then undo it (swap back). Like trying different paths in a maze and backing up when we hit a dead end. - -### 3. **Permutations** -We generate every possible ordering of the cities. It's like having cards numbered 1,2,3 and arranging them in every possible order: 123, 132, 213, 231, 312, 321. - -### 4. **Global Optimization** -We keep track of the best solution found so far and update it whenever we find something better. - -## Fun Facts! 🎯 - -1. **This problem is NP-hard** - that's computer science speak for "really, really hard to solve quickly" - -2. **Real delivery companies** use approximate algorithms that find "pretty good" solutions much faster than perfect ones - -3. **With 11 cities** (the maximum our program handles), there are 39,916,800 possible routes! - -4. **The problem has applications** in: - - GPS navigation systems - - Circuit board manufacturing - - DNA sequencing - - Planning efficient tours for bands or sports teams - -## Summary 📝 - -Our TSP algorithm is like a very thorough friend who insists on checking every possible pizza delivery route before deciding which one is shortest. It's not the fastest approach, but it guarantees the perfect answer! - -The main steps are: -1. **Read** all the city locations -2. **Generate** every possible visiting order -3. **Calculate** the total distance for each order -4. **Remember** the shortest distance found -5. **Print** the answer - -Even though it's not the most efficient algorithm for large problems, it's perfect for learning because it's easy to understand and always gives the correct answer! 🎉 \ No newline at end of file