En aquesta secció, treballarem en diversos exercicis pràctics per aplicar les estratègies de disseny d'algorismes que hem après en els mòduls anteriors. Cada exercici inclou una descripció del problema, una solució suggerida i una explicació detallada del codi.
Exercici 1: Divideix i Venceràs
Descripció del Problema
Implementa un algorisme que trobi l'element màxim en una llista de nombres utilitzant l'estratègia de "Divideix i Venceràs".
Solució Suggerida
def find_max(arr, low, high):
# Cas base: Si la llista té un sol element
if low == high:
return arr[low]
# Divideix la llista en dues meitats
mid = (low + high) // 2
# Troba el màxim en cada meitat
max1 = find_max(arr, low, mid)
max2 = find_max(arr, mid + 1, high)
# Retorna el màxim dels dos
return max(max1, max2)
# Exemple d'ús
arr = [1, 5, 3, 9, 2, 8, 4]
resultat = find_max(arr, 0, len(arr) - 1)
print(f"L'element màxim és: {resultat}")Explicació
- Cas Base: Si la llista té un sol element, aquest és el màxim.
- Divisió: Divideix la llista en dues meitats.
- Conquesta: Troba el màxim en cada meitat recursivament.
- Combinació: Retorna el màxim dels dos valors obtinguts.
Exercici 2: Algorismes Greedy
Descripció del Problema
Implementa un algorisme greedy per resoldre el problema de la motxilla fraccionària.
Solució Suggerida
class Objecte:
def __init__(self, pes, valor):
self.pes = pes
self.valor = valor
self.ratio = valor / pes
def motxilla_fraccionaria(objectes, capacitat):
# Ordena els objectes per valor per unitat de pes en ordre descendent
objectes.sort(key=lambda x: x.ratio, reverse=True)
valor_total = 0
for obj in objectes:
if capacitat >= obj.pes:
capacitat -= obj.pes
valor_total += obj.valor
else:
valor_total += obj.valor * (capacitat / obj.pes)
break
return valor_total
# Exemple d'ús
objectes = [Objecte(10, 60), Objecte(20, 100), Objecte(30, 120)]
capacitat = 50
resultat = motxilla_fraccionaria(objectes, capacitat)
print(f"El valor màxim que es pot obtenir és: {resultat}")Explicació
- Ordenació: Ordena els objectes per valor per unitat de pes en ordre descendent.
- Selecció: Afegeix els objectes a la motxilla fins que la capacitat es completi.
- Fraccionament: Si no hi ha prou capacitat per a un objecte sencer, afegeix una fracció d'aquest.
Exercici 3: Programació Dinàmica
Descripció del Problema
Implementa un algorisme per resoldre el problema del camí mínim en una matriu utilitzant programació dinàmica.
Solució Suggerida
def cami_minim(mat):
if not mat or not mat[0]:
return 0
n, m = len(mat), len(mat[0])
dp = [[0] * m for _ in range(n)]
dp[0][0] = mat[0][0]
# Inicialitza la primera fila
for j in range(1, m):
dp[0][j] = dp[0][j-1] + mat[0][j]
# Inicialitza la primera columna
for i in range(1, n):
dp[i][0] = dp[i-1][0] + mat[i][0]
# Omple la resta de la matriu dp
for i in range(1, n):
for j in range(1, m):
dp[i][j] = mat[i][j] + min(dp[i-1][j], dp[i][j-1])
return dp[n-1][m-1]
# Exemple d'ús
matriu = [
[1, 3, 1],
[1, 5, 1],
[4, 2, 1]
]
resultat = cami_minim(matriu)
print(f"El camí mínim té un cost de: {resultat}")Explicació
- Inicialització: Crea una matriu
dpper emmagatzemar els costos mínims. - Primera Fila i Columna: Inicialitza la primera fila i columna amb els costos acumulats.
- Ompliment: Omple la resta de la matriu
dpamb els costos mínims acumulats fins a cada cel·la. - Resultat: El valor a
dp[n-1][m-1]és el cost mínim per arribar a la cantonada inferior dreta.
Exercici 4: Backtracking
Descripció del Problema
Implementa un algorisme per resoldre el problema de les N reines utilitzant backtracking.
Solució Suggerida
def es_segura(board, row, col, n):
# Comprova la fila a l'esquerra
for i in range(col):
if board[row][i] == 1:
return False
# Comprova la diagonal superior esquerra
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False
# Comprova la diagonal inferior esquerra
for i, j in zip(range(row, n, 1), range(col, -1, -1)):
if board[i][j] == 1:
return False
return True
def soluciona_n_reines(board, col, n):
if col >= n:
return True
for i in range(n):
if es_segura(board, i, col, n):
board[i][col] = 1
if soluciona_n_reines(board, col + 1, n):
return True
board[i][col] = 0
return False
def n_reines(n):
board = [[0] * n for _ in range(n)]
if not soluciona_n_reines(board, 0, n):
return "No hi ha solució"
return board
# Exemple d'ús
n = 4
resultat = n_reines(n)
for fila in resultat:
print(fila)Explicació
- Funció de Seguretat: Comprova si és segur col·locar una reina en una posició específica.
- Backtracking: Col·loca les reines una per una i utilitza backtracking per trobar una solució.
- Solució: Si es troba una solució, es retorna el tauler amb les reines col·locades.
Conclusió
En aquesta secció, hem treballat amb diversos exercicis pràctics que ens han permès aplicar les estratègies de disseny d'algorismes que hem après. Hem vist com utilitzar "Divideix i Venceràs", algorismes greedy, programació dinàmica i backtracking per resoldre problemes comuns. Aquests exercicis ens ajuden a entendre millor com dissenyar algorismes eficients i eficaços.
Curs d'Anàlisi i Disseny d'Algorismes
Mòdul 1: Introducció als Algorismes
Mòdul 2: Anàlisi d'Algorismes
- Anàlisi de Complexitat Temporal
- Anàlisi de Complexitat Espacial
- Cases de Complexitat: Millor, Pitjor i Promig
Mòdul 3: Estratègies de Disseny d'Algorismes
Mòdul 4: Algorismes Clàssics
- Cerca Binària
- Ordenació per Inserció
- Ordenació per Mescla (Merge Sort)
- Ordenació Ràpida (Quick Sort)
- Algorisme de Dijkstra
- Algorisme de Floyd-Warshall
