Replaced subject resolutions of level-2 with backtracking

This commit is contained in:
Rui Ribeiro 2025-09-25 08:16:02 +01:00
parent 0ec1385e07
commit 94ff9315d7
9 changed files with 521 additions and 191 deletions

View File

@ -1,84 +1,131 @@
#include "n_queens.h"
#include <unistd.h> // write
#include <stdlib.h> // malloc, free, atoi
#include <stdio.h> // fprintf, stderr (permitido para erros)
void print_number(int n)
// Função auxiliar para escrever um único caractere no stdout
void ft_putchar(char c)
{
char c;
if (n >= 10)
print_number(n / 10);
c = '0' + (n % 10);
write(1, &c, 1);
}
void print_solution(int *queens, int n)
// Função auxiliar para escrever um número inteiro no stdout
void ft_putnbr(int n)
{
int i;
i = 0;
while (i < n)
if (n >= 10)
{
if (i > 0)
write(1, " ", 1);
print_number(queens[i]);
i++;
ft_putnbr(n / 10);
}
write(1, "\n", 1);
ft_putchar((n % 10) + '0');
}
int is_safe(int *queens, int row, int col, int n)
// Imprime a solução no formato "p1 p2 p3 ..."
void print_solution(int *board, int n)
{
int i;
(void)n;
i = 0;
while (i < row)
int i = 0;
while (i < n)
{
if (queens[i] == col)
ft_putnbr(board[i]);
if (i < n - 1)
ft_putchar(' ');
i++;
}
ft_putchar('\n');
}
int ft_abs(int nbr)
{
if (nbr < 0)
return (nbr * -1);
else
return (nbr);
}
// Verifica se é seguro colocar uma rainha na posição (row, col)
// Só precisamos verificar as colunas à esquerda da coluna atual
int is_safe(int *board, int n, int row, int col)
{
int i;
i = 0;
(void)n;
while (i < col)
{
// 1. Verifica se há outra rainha na mesma linha
if (board[i] == row)
return (0);
if (queens[i] - i == col - row)
return (0);
if (queens[i] + i == col + row)
// 2. Verifica as diagonais
// abs(board[i] - row) == abs(i - col)
// Como col > i, abs(i - col) é (col - i)
if (ft_abs(board[i] - row) == (col - i))
return (0);
i++;
}
// Se passou por todas as verificações, a posição é segura
return (1);
}
void solve_nqueens(int *queens, int row, int n)
// A função principal de backtracking
void solve(int *board, int n, int col)
{
int col;
if (row == n)
// Caso base: Se todas as rainhas foram colocadas (chegamos ao final do tabuleiro)
if (col == n)
{
print_solution(queens, n);
return ;
print_solution(board, n);
return;
}
col = 0;
while (col < n)
// Tenta colocar uma rainha em cada linha da coluna atual
int row = 0;
while (row < n)
{
if (is_safe(queens, row, col, n))
// Verifica se a posição (row, col) é segura
if (is_safe(board, n, row, col))
{
queens[row] = col;
solve_nqueens(queens, row + 1, n);
// Coloca a rainha
board[col] = row;
// Chama a função recursivamente para a próxima coluna
solve(board, n, col + 1);
// O backtracking é implícito: na próxima iteração do loop,
// board[col] será sobrescrito com um novo valor de 'row'.
// Não precisamos de "remover" a rainha explicitamente.
}
col++;
row++;
}
}
int main(int argc, char **argv)
int main(int argc, char **argv)
{
int n;
int *queens;
int *board;
int n;
if (argc != 2)
{
// Usar fprintf com stderr é permitido para mensagens de erro
fprintf(stderr, "Usage: %s n\n", argv[0]);
return (1);
}
n = atoi(argv[1]);
if (n <= 0)
{
return (0); // Não faz nada para n <= 0, como esperado
}
// Aloca memória para o tabuleiro. board[col] = row
board = (int *)malloc(sizeof(int) * n);
if (!board)
{
fprintf(stderr, "Memory allocation failed\n");
return (1);
queens = (int *)malloc(sizeof(int) * n);
if (!queens)
return (1);
solve_nqueens(queens, 0, n);
free(queens);
}
// Inicia o processo de backtracking a partir da primeira coluna (col = 0)
solve(board, n, 0);
// Liberta a memória alocada
free(board);
return (0);
}

View File

@ -1,13 +0,0 @@
#ifndef N_QUEENS_H
# define N_QUEENS_H
# include <stdlib.h>
# include <unistd.h>
# include <stdio.h>
int is_safe(int *queens, int row, int col, int n);
void solve_nqueens(int *queens, int row, int n);
void print_solution(int *queens, int n);
void print_number(int n);
#endif

View File

@ -0,0 +1,93 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
void ft_putnbr(int n)
{
if (n >= 10)
ft_putnbr(n / 10);
ft_putchar((n % 10) + '0');
}
void print_solution(int *board, int n)
{
int i = 0;
while (i < n)
{
ft_putnbr(board[i]);
if (i < n - 1)
ft_putchar(' ');
i++;
}
ft_putchar('\n');
}
int ft_abs(int nbr)
{
if (nbr < 0)
return (nbr * -1);
else
return (nbr);
}
int is_safe(int *board, int n, int row, int col)
{
int i;
i = 0;
(void)n;
while (i < col)
{
if (board[i] == row)
return (0);
if (ft_abs(board[i] - row) == (col - i))
return (0);
i++;
}
return (1);
}
void solve(int *board, int n, int col)
{
if (col == n)
return(print_solution(board, n));
int row = 0;
while (row < n)
{
if (is_safe(board, n, row, col))
{
board[col] = row;
solve(board, n, col + 1);
}
row++;
}
}
int main(int argc, char **argv)
{
int *board;
int n;
if (argc != 2)
return (fprintf(stderr, "Usage: %s n\n", argv[0]), 1);
n = atoi(argv[1]);
if (n <= 0)
return (0);
board = (int *)malloc(sizeof(int) * n);
if (!board)
{
fprintf(stderr, "Memory allocation failed\n");
return (1);
}
solve(board, n, 0);
free(board);
return (0);
}

View File

@ -0,0 +1,94 @@
#include <unistd.h> // write
#include <stdlib.h> // malloc, free
int ft_strlen(const char *s)
{
int len = 0;
while (s[len])
len++;
return (len);
}
void print_solution(const char *s)
{
write(1, s, ft_strlen(s));
write(1, "\n", 1);
}
void ft_swap(char *a, char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
void sort_string(char *str, int n)
{
int i;
int j;
i = 0;
while (i < n - 1)
{
j = 0;
while (j < n - i - 1)
{
if (str[j] > str[j + 1])
ft_swap(&str[j], &str[j + 1]);
j++;
}
i++;
}
}
void generate_permutations(char *original_str, char *current_perm, int *used, int n, int k)
{
if (k == n)
{
print_solution(current_perm);
return;
}
int i = 0;
while (i < n)
{
if (used[i] == 0)
{
current_perm[k] = original_str[i];
used[i] = 1;
generate_permutations(original_str, current_perm, used, n, k + 1);
used[i] = 0;
}
i++;
}
}
int main(int argc, char **argv)
{
if (argc != 2 || !argv[1][0])
return (0);
int n = ft_strlen(argv[1]);
char *original_str = argv[1];
sort_string(original_str, n);
char *current_perm = (char *)malloc(sizeof(char) * (n + 1));
if (!current_perm)
return (1);
current_perm[n] = '\0';
int *used = (int *)calloc(sizeof(int) * n);
if (!used)
{
free(current_perm);
return (1);
}
generate_permutations(original_str, current_perm, used, n, 0);
free(current_perm);
free(used);
return (0);
}

View File

@ -1,98 +1,132 @@
#include "permutations.h"
#include <unistd.h> // write
#include <stdlib.h> // malloc, free
int ft_strlen(char *str)
// Função auxiliar para obter o comprimento de uma string
int ft_strlen(const char *s)
{
int len;
len = 0;
while (str[len])
int len = 0;
while (s[len])
len++;
return (len);
}
void ft_swap(char *a, char *b)
// Função auxiliar para imprimir uma string seguida de uma nova linha
void print_solution(const char *s)
{
char temp;
write(1, s, ft_strlen(s));
write(1, "\n", 1);
}
temp = *a;
// Função auxiliar para trocar dois caracteres (usada na ordenação)
void ft_swap(char *a, char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
void ft_sort_string(char *str)
// Função para ordenar a string de entrada (Bubble Sort simples)
// Isso é crucial para garantir que as permutações sejam geradas em ordem alfabética.
void sort_string(char *str, int n)
{
int i;
int j;
int len;
int i;
int j;
len = ft_strlen(str);
i = 0;
while (i < len - 1)
while (i < n - 1)
{
j = i + 1;
while (j < len)
j = 0;
while (j < n - i - 1)
{
if (str[i] > str[j])
ft_swap(&str[i], &str[j]);
if (str[j] > str[j + 1])
ft_swap(&str[j], &str[j + 1]);
j++;
}
i++;
}
}
void ft_reverse(char *str, int start, int end)
// A função principal de backtracking, análoga à sua função `solve`
// Parâmetros:
// - original_str: A string original, já ordenada
// - current_perm: A permutação que estamos construindo (o "tabuleiro")
// - used: Um array que marca quais caracteres da string original já foram usados
// - n: O tamanho da string
// - k: A posição atual que estamos preenchendo em `current_perm` (análogo a `col`)
void generate_permutations(char *original_str, char *current_perm, int *used, int n, int k)
{
while (start < end)
// Caso base: Se todas as posições foram preenchidas (k == n),
// análogo a `if (col == n)` no N-Queens.
if (k == n)
{
ft_swap(&str[start], &str[end]);
start++;
end--;
print_solution(current_perm);
return;
}
}
int next_permutation(char *str, int len)
{
int i;
int j;
i = len - 2;
while (i >= 0 && str[i] >= str[i + 1])
i--;
if (i < 0)
return (0);
j = len - 1;
while (str[j] <= str[i])
j--;
ft_swap(&str[i], &str[j]);
ft_reverse(str, i + 1, len - 1);
return (1);
}
int main(int argc, char **argv)
{
char *str;
char *copy;
int len;
int i;
if (argc != 2)
return (1);
str = argv[1];
len = ft_strlen(str);
copy = malloc(sizeof(char) * (len + 1));
if (!copy)
return (1);
i = 0;
while (i < len)
// Tenta colocar cada caractere da string original na posição 'k' atual
int i = 0;
while (i < n)
{
copy[i] = str[i];
// Verifica se a "jogada" é válida, análogo ao `is_safe`.
// A condição é: o i-ésimo caractere da string original ainda não foi usado?
if (used[i] == 0)
{
// "Coloca a peça no tabuleiro":
// 1. Coloca o caractere na permutação atual
current_perm[k] = original_str[i];
// 2. Marca este caractere como usado
used[i] = 1;
// Chama a função recursivamente para a próxima posição (k + 1),
// análogo a `solve(board, n, col + 1)`
generate_permutations(original_str, current_perm, used, n, k + 1);
// Backtracking: Desfaz a jogada para que este caractere possa ser
// usado em uma posição diferente na próxima ramificação da recursão.
// Isso é crucial e um pouco mais explícito que no seu N-Queens.
used[i] = 0;
}
i++;
}
copy[len] = '\0';
ft_sort_string(copy);
puts(copy);
while (next_permutation(copy, len))
puts(copy);
free(copy);
}
int main(int argc, char **argv)
{
if (argc != 2 || !argv[1][0])
{
// Não faz nada se não houver exatamente um argumento ou se for uma string vazia.
return (0);
}
int n = ft_strlen(argv[1]);
char *original_str = argv[1]; // Não precisamos de cópia, podemos ordenar o argumento
// 1. Ordena a string de entrada para garantir a saída em ordem alfabética
sort_string(original_str, n);
// 2. Aloca memória para a permutação que será construída
char *current_perm = (char *)malloc(sizeof(char) * (n + 1));
if (!current_perm)
return (1);
current_perm[n] = '\0'; // Garante a terminação da string
// 3. Aloca memória para o array de "usados" e o inicializa com 0
// `calloc` é útil aqui pois inicializa a memória com zeros.
int *used = (int *)malloc(sizeof(int) * n);
if (!used)
{
free(current_perm);
return (1);
}
for (int i = 0; i < n; i++)
used[i] = 0;
// Inicia o processo de backtracking a partir da primeira posição (k = 0)
generate_permutations(original_str, current_perm, used, n, 0);
// Liberta a memória alocada
free(current_perm);
free(used);
return (0);
}

View File

@ -1,14 +0,0 @@
#ifndef PERMUTATIONS_H
# define PERMUTATIONS_H
# include <stdlib.h>
# include <unistd.h>
# include <stdio.h>
void ft_swap(char *a, char *b);
void ft_sort_string(char *str);
void ft_reverse(char *str, int start, int end);
int next_permutation(char *str, int len);
int ft_strlen(char *str);
#endif

View File

@ -1,5 +1,8 @@
#include "powerset.h"
#include <stdio.h>
#include <stdlib.h>
// Função auxiliar para imprimir um subconjunto encontrado.
// A ordem dos elementos é mantida porque os adicionamos na ordem original.
void print_subset(int *subset, int size)
{
int i;
@ -15,59 +18,89 @@ void print_subset(int *subset, int size)
printf("\n");
}
void find_subsets(int *set, int set_size, int target, int *subset,
int subset_size, int index)
/**
* Função recursiva de backtracking para encontrar os subconjuntos.
*
* @param target A soma restante que precisamos alcançar.
* @param set O conjunto original de números.
* @param set_size O tamanho do conjunto original.
* @param index O índice do elemento atual que estamos considerando no conjunto.
* @param current_path O subconjunto que estamos construindo atualmente.
* @param path_len O número de elementos no subconjunto atual.
*/
void find_subsets(int target, int *set, int set_size, int index, int *current_path, int path_len)
{
int current_sum;
int i;
if (index == set_size)
// Caso base 1: Encontramos um subconjunto cuja soma é igual ao alvo original.
if (target == 0)
{
current_sum = 0;
i = 0;
while (i < subset_size)
{
current_sum += subset[i];
i++;
}
if (current_sum == target)
print_subset(subset, subset_size);
return ;
print_subset(current_path, path_len);
// Retornamos porque encontramos uma solução válida para este caminho.
return;
}
find_subsets(set, set_size, target, subset, subset_size, index + 1);
subset[subset_size] = set[index];
find_subsets(set, set_size, target, subset, subset_size + 1, index + 1);
// Caso base 2: Se já consideramos todos os números, não há mais nada a fazer.
if (index >= set_size)
{
return;
}
// --- Decisão Recursiva ---
// Escolha 1: INCLUIR o elemento atual (set[index]) no subconjunto.
// Adicionamos o elemento ao nosso caminho.
current_path[path_len] = set[index];
// Chamamos a função para o próximo elemento, com o alvo reduzido.
find_subsets(target - set[index], set, set_size, index + 1, current_path, path_len + 1);
// Escolha 2: NÃO INCLUIR o elemento atual (set[index]).
// Este é o passo de "backtrack". Nós simplesmente ignoramos o elemento atual
// e passamos para o próximo, sem alterar o caminho ou o alvo.
find_subsets(target, set, set_size, index + 1, current_path, path_len);
}
int main(int argc, char **argv)
{
int *set;
int *subset;
int target;
int set_size;
int i;
int target;
int *numbers;
int *path;
int size;
int i;
// Deve haver pelo menos um alvo (n). O conjunto (s) pode ser vazio.
if (argc < 2)
return (1);
return (0);
target = atoi(argv[1]);
set_size = argc - 2;
set = malloc(sizeof(int) * set_size);
if (!set)
return (1);
subset = malloc(sizeof(int) * set_size);
if (!subset)
size = argc - 2;
// Aloca memória para o conjunto de números de entrada.
numbers = (int *)malloc(sizeof(int) * size);
if (!numbers)
return (1); // Erro de malloc
// Aloca memória para o array que armazenará o subconjunto atual.
// O tamanho máximo de um subconjunto é o tamanho do conjunto original.
path = (int *)malloc(sizeof(int) * size);
if (!path)
{
free(set);
return (1);
free(numbers);
return (1); // Erro de malloc
}
// Converte os argumentos da linha de comando em um array de inteiros.
i = 0;
while (i < set_size)
while (i < size)
{
set[i] = atoi(argv[i + 2]);
numbers[i] = atoi(argv[i + 2]);
i++;
}
find_subsets(set, set_size, target, subset, 0, 0);
free(set);
free(subset);
// Inicia a busca recursiva a partir do primeiro elemento (índice 0).
find_subsets(target, numbers, size, 0, path, 0);
// Libera a memória alocada.
free(numbers);
free(path);
return (0);
}

View File

@ -1,12 +0,0 @@
#ifndef POWERSET_H
# define POWERSET_H
# include <stdlib.h>
# include <unistd.h>
# include <stdio.h>
void find_subsets(int *set, int set_size, int target, int *subset,
int subset_size, int index);
void print_subset(int *subset, int size);
#endif

View File

@ -0,0 +1,68 @@
#include <stdio.h>
#include <stdlib.h>
void print_subset(int *subset, int size)
{
int i;
i = 0;
while (i < size)
{
printf("%d", subset[i]);
if (i < size - 1)
printf(" ");
i++;
}
printf("\n");
}
void find_subsets(int target, int *set, int set_size, int index, int *current_path, int path_len)
{
if (target == 0)
return (print_subset(current_path, path_len));
if (index >= set_size)
return;
current_path[path_len] = set[index];
find_subsets(target - set[index], set, set_size, index + 1, current_path, path_len + 1);
find_subsets(target, set, set_size, index + 1, current_path, path_len);
}
int main(int argc, char **argv)
{
int target;
int *numbers;
int *path;
int size;
int i;
if (argc < 2)
return (0);
target = atoi(argv[1]);
size = argc - 2;
numbers = (int *)malloc(sizeof(int) * size);
if (!numbers)
return (1);
path = (int *)malloc(sizeof(int) * size);
if (!path)
{
free(numbers);
return (1);
}
i = 0;
while (i < size)
{
numbers[i] = atoi(argv[i + 2]);
i++;
}
find_subsets(target, numbers, size, 0, path, 0);
free(numbers);
free(path);
return (0);
}