Edited README's, added broken_gnl exercise, LICENSE and main README
This commit is contained in:
parent
4b8a2c7481
commit
f1b9d38007
72
LICENSE
Normal file
72
LICENSE
Normal file
@ -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.
|
||||||
125
README.md
Normal file
125
README.md
Normal file
@ -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 <pattern>`
|
||||||
|
- 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 <string>`
|
||||||
|
|
||||||
|
- powerset (level-2/powerset)
|
||||||
|
- Purpose: list all subsets of the provided set whose elements sum to the
|
||||||
|
target integer (first argument).
|
||||||
|
- Usage: `./powerset <target> <list of distinct integers>`
|
||||||
|
|
||||||
|
- 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
|
||||||
97
level-1/broken_gnl/broken_gnl.c
Normal file
97
level-1/broken_gnl/broken_gnl.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include "broken_gnl.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@ -1,24 +1,4 @@
|
|||||||
/*
|
#define _GNU_SOURCE
|
||||||
* 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()
|
|
||||||
#ifndef BUFFER_SIZE
|
#ifndef BUFFER_SIZE
|
||||||
# define BUFFER_SIZE 42
|
# define BUFFER_SIZE 42
|
||||||
#endif
|
#endif
|
||||||
@ -29,42 +9,30 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Función para encontrar y reemplazar todas las ocurrencias
|
|
||||||
void ft_filter(char *buffer, const char *target)
|
void ft_filter(char *buffer, const char *target)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int target_len = strlen(target);
|
int target_len = strlen(target);
|
||||||
int j, k;
|
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])
|
while (buffer[i])
|
||||||
{
|
{
|
||||||
j = 0;
|
j = 0;
|
||||||
// Verificar si hay coincidencia desde la posición actual
|
|
||||||
while (target[j] && (buffer[i + j] == target[j]))
|
while (target[j] && (buffer[i + j] == target[j]))
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
if (j == target_len) // Coincidencia completa encontrada
|
if (j == target_len)
|
||||||
{
|
{
|
||||||
// Escribir asteriscos en lugar del patrón
|
|
||||||
k = 0;
|
k = 0;
|
||||||
while (k < target_len)
|
while (k < target_len)
|
||||||
{
|
{
|
||||||
write(1, "*", 1);
|
write(1, "*", 1);
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
i += target_len; // Saltar el patrón completo
|
i += target_len;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No hay coincidencia, escribir carácter original
|
|
||||||
write(1, &buffer[i], 1);
|
write(1, &buffer[i], 1);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -73,30 +41,18 @@ void ft_filter(char *buffer, const char *target)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
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')
|
if (argc != 2 || argv[1][0] == '\0')
|
||||||
return 1;
|
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 temp[BUFFER_SIZE];
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int total_read = 0;
|
int total_read = 0;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
|
|
||||||
// Leer de stdin hasta EOF
|
|
||||||
while ((bytes = read(0, temp, BUFFER_SIZE)) > 0)
|
while ((bytes = read(0, temp, BUFFER_SIZE)) > 0)
|
||||||
{
|
{
|
||||||
// Expandir el buffer principal para acomodar los nuevos datos
|
|
||||||
buffer = realloc(result, total_read + bytes + 1);
|
buffer = realloc(result, total_read + bytes + 1);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
{
|
{
|
||||||
@ -107,13 +63,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
result = buffer;
|
result = buffer;
|
||||||
|
|
||||||
// Copiar los nuevos datos al buffer principal
|
|
||||||
memmove(result + total_read, temp, bytes);
|
memmove(result + total_read, temp, bytes);
|
||||||
total_read += bytes;
|
total_read += bytes;
|
||||||
result[total_read] = '\0'; // Asegurar terminación
|
result[total_read] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar errores de lectura
|
|
||||||
if (bytes < 0)
|
if (bytes < 0)
|
||||||
{
|
{
|
||||||
perror("read");
|
perror("read");
|
||||||
@ -121,38 +75,10 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si no se leyó nada, salir sin error
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Procesar el buffer y aplicar el filtro
|
|
||||||
ft_filter(result, argv[1]);
|
ft_filter(result, argv[1]);
|
||||||
|
|
||||||
// Liberar memoria
|
|
||||||
free(result);
|
free(result);
|
||||||
return 0;
|
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
|
|
||||||
*/
|
|
||||||
@ -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 <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
// 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
|
|
||||||
*/
|
|
||||||
@ -2,67 +2,47 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 match_space(FILE *f)
|
||||||
{
|
{
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
// Lê caracteres enquanto não for o fim do ficheiro e forem espaços
|
|
||||||
while ((ch = fgetc(f)) != EOF && isspace(ch))
|
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)
|
if (ch != EOF)
|
||||||
ungetc(ch, f);
|
ungetc(ch, f);
|
||||||
|
|
||||||
// Verifica se ocorreu um erro de leitura durante as operações
|
|
||||||
if (ferror(f))
|
if (ferror(f))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
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 match_char(FILE *f, char c)
|
||||||
{
|
{
|
||||||
int ch = fgetc(f);
|
int ch = fgetc(f);
|
||||||
if (ch == c)
|
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)
|
if (ch != EOF)
|
||||||
ungetc(ch, f);
|
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 scan_char(FILE *f, va_list ap)
|
||||||
{
|
{
|
||||||
int ch = fgetc(f);
|
int ch = fgetc(f);
|
||||||
char *cp = va_arg(ap, char *);
|
char *cp = va_arg(ap, char *);
|
||||||
|
|
||||||
if (ch == EOF)
|
if (ch == EOF)
|
||||||
return -1; // Falha se chegar ao fim do ficheiro
|
return -1;
|
||||||
|
|
||||||
*cp = (char)ch;
|
*cp = (char)ch;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Lê um inteiro do stream, ignorando espaços iniciais e tratando o sinal.
|
|
||||||
*/
|
|
||||||
int scan_int(FILE *f, va_list ap)
|
int scan_int(FILE *f, va_list ap)
|
||||||
{
|
{
|
||||||
int *ip = va_arg(ap, int *);
|
int *ip = va_arg(ap, int *);
|
||||||
@ -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**.
|
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:
|
A queen is the strongest piece in chess. She can move:
|
||||||
- **Horizontally** (left and right) ←→
|
- **Horizontally** (left and right)
|
||||||
- **Vertically** (up and down) ↕️
|
- **Vertically** (up and down)
|
||||||
- **Diagonally** (in any diagonal direction) ↗️↖️↙️↘️
|
- **Diagonally** (in any diagonal direction)
|
||||||
|
|
||||||
She can move as many squares as she wants in these directions!
|
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):
|
Let's say we want to solve the **4-Queens problem** (N=4):
|
||||||
- We have a 4×4 chessboard (16 squares total)
|
- We have a 4×4 chessboard (16 squares total)
|
||||||
@ -45,208 +45,4 @@ Q . . . ← Queen attacks diagonally
|
|||||||
. . . X
|
. . . X
|
||||||
```
|
```
|
||||||
|
|
||||||
So we can't put any other queen in any of those X positions!
|
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! 🚀
|
|
||||||
@ -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."
|
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!
|
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:
|
Your mission is to write a program that:
|
||||||
1. Takes a word (like "abc")
|
1. Takes a word (like "abc")
|
||||||
2. Shows ALL possible ways to rearrange the letters
|
2. Shows ALL possible ways to rearrange the letters
|
||||||
3. Shows them in **alphabetical order** (like in a dictionary)
|
3. Shows them in **alphabetical order** (like in a dictionary)
|
||||||
|
|
||||||
## Real Examples 📝
|
## Real Examples
|
||||||
|
|
||||||
Let's see what our program does:
|
Let's see what our program does:
|
||||||
|
|
||||||
@ -54,147 +54,3 @@ bca
|
|||||||
cab
|
cab
|
||||||
cba
|
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!*
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# Powerset Exercise - Simple Explanation 🧮
|
# Powerset
|
||||||
|
|
||||||
## What is this exercise about?
|
## 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!
|
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**
|
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**.
|
And you want to find all the ways to pick balls that add up to **5**.
|
||||||
|
|
||||||
Here are all the possible ways:
|
Here are all the possible ways:
|
||||||
- Pick just ball **5** → 5 = 5 ✅
|
- Pick just ball **5** → 5 = 5
|
||||||
- Pick balls **1** and **4** → 1 + 4 = 5 ✅
|
- Pick balls **1** and **4** → 1 + 4 = 5
|
||||||
- Pick balls **2** and **3** → 2 + 3 = 5 ✅
|
- Pick balls **2** and **3** → 2 + 3 = 5
|
||||||
|
|
||||||
So the answer would be:
|
So the answer would be:
|
||||||
```
|
```
|
||||||
@ -23,153 +23,3 @@ So the answer would be:
|
|||||||
1 4
|
1 4
|
||||||
2 3
|
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?* 🤖✨
|
|
||||||
@ -26,154 +26,8 @@ This means:
|
|||||||
1. Open door 1: `(`
|
1. Open door 1: `(`
|
||||||
2. Open door 2: `(`
|
2. Open door 2: `(`
|
||||||
3. Close 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:
|
To fix this, we need to either:
|
||||||
- Remove one of the opening doors: ` ()` (remove first `(`)
|
- Remove one of the opening doors: ` ()` (remove first `(`)
|
||||||
- Or remove one of the opening doors: `( )` (remove second `(`)
|
- 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!
|
|
||||||
36
level-2/tsp/README.md
Normal file
36
level-2/tsp/README.md
Normal file
@ -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!
|
||||||
@ -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! 🎉
|
|
||||||
Loading…
Reference in New Issue
Block a user