ALGORÍTMICA Dpto. O.E.I. – U.P.M. 2/SEP/09 1º) (3'5 Puntos) El Marshall de un torneo de golf es la persona que asiste a los jugadores, conservando el buen flujo de los mismos en el campo y aplicando el reglamento. Como preparación previa al torneo se encarga de revisar el estado de los hoyos y verificar el color de cada bandera, lo que indica por lo general la posición de la bandera en el green o sea, si esta adelante (roja), en el medio (blanca), o atrás (azul). SE PIDE: Escribir un algoritmo en Pascal que calcule cuál es la ruta que debe seguir el Marshall para realizar la verificación de todas las banderas de los 18 hoyos, realizando el menor recorrido posible. El esquema de hoyos del campo está representado por un grafo no dirigido valorado implementado con una matriz de valores que indican el número de metros existentes entre los hoyos, pudiendo ser MAXINT si no hay camino entre los hoyos. NOTA: el Marshall parte del hoyo 1 y finaliza su recorrido en el hoyo 1. CONST NMax = 18; (* Número máximo hoyos recorrido *) FichRecorrido = 'campo18.txt'; Infinito = 10000; DistanciaMaxima = 2000; (* Distancia máxima que se puede recorrer *) TYPE tRecorrido = ARRAY [1..NMax, 1..NMax] OF Integer; (* Matriz con distancias entre hoyos *) tVector = ARRAY [1..NMax] OF Integer; tPila = ^Registro; Registro = RECORD hoyo: Integer; sig: tPila END; VAR Recorrido: tRecorrido; Comprobado: tVector; v, N, distOptima: Integer; Camino, CaminoOpt: tPila; PROCEDURE IniPila(VAR P: tPila); BEGIN P := NIL; END; (* IniPila *) FUNCTION PilaVacia(P: tPila): Boolean; BEGIN PilaVacia := (P = NIL) END; (* PilaVacia *) PROCEDURE Apilar(VAR P: tPila; hoyo: Integer); VAR aux: tPila; BEGIN NEW(aux); aux^.hoyo := hoyo; aux^.sig := P; P := aux; END; (* Apilar *) PROCEDURE Desapilar(VAR P: tPila; VAR hoyo: Integer); VAR aux: tPila; BEGIN IF PilaVacia(P) THEN WriteLn('ERROR: Pila Vac¡a') ELSE BEGIN hoyo := P^.hoyo; aux := P; P := P^.sig; Dispose(aux); END END; (* Desapilar *) PROCEDURE VaciarPila(VAR P: tPila); VAR aux: Integer; BEGIN WHILE NOT PilaVacia(P) DO Desapilar(P, aux) END; (* Vaciar Pila *) PROCEDURE CopiarPila(VAR pOrigen, pDestino: tPila); VAR hoyo: Integer; BEGIN IF NOT PilaVacia(pOrigen) THEN BEGIN Desapilar(pOrigen, hoyo); CopiarPila(pOrigen, pDestino); Apilar(pOrigen, hoyo); Apilar(pDestino, hoyo); END END; (* CopiarPila *) PROCEDURE CargarMatrizDistancias(VAR Recorrido: tRecorrido); VAR Fichero: Text; h1, h2, dist: Integer; BEGIN FOR h1 := 1 TO NMax DO FOR h2 := 1 TO NMax DO Recorrido[h1][h2] := Infinito; FOR h1 := 1 TO NMax DO Recorrido[h1][h1] := 0; Universidad Politécnica de Madrid – E.U. Informática Página 1 assign(Fichero, FichRecorrido); Reset(Fichero); ReadLn(Fichero, N); WHILE NOT EoF(Fichero) DO BEGIN ReadLn(Fichero, h1, h2, dist); IF (h1 < 0) OR (h1 > N) OR (h2 < 0) OR (h2 > N) OR (dist < 0) THEN WriteLn('Error en lectura de datos', h1, h2, dist); Recorrido[h1][h2] := dist; Recorrido[h2][h1] := dist; END; Close(Fichero) END; (* CargarMatrizDistancias *) (* Recorrido Comprobado -> todos distinto 0 *) FUNCTION RecorridoComprobado(Comprobado: tVector): Boolean; VAR v: Integer; TodosDistintosCero: Boolean; BEGIN v := 2; TodosDistintosCero := TRUE; WHILE (v <= N) AND TodosDistintosCero DO BEGIN TodosDistintosCero := (Comprobado[v] <> 0); v := v + 1 END; RecorridoComprobado := TodosDistintosCero END; (* RecorridoComprobado *) (* Algoritmo de selección óptima *) PROCEDURE ComprobarBanderas(VAR Recorrido: tRecorrido; (* se pasa por ref. para ahorrar memoria *) hoyoActual, distanciaMaxima, distActual: Integer; VAR distOptima: Integer; VAR Comprobado: tVector; VAR Camino, CaminoOpt: tPila); VAR aux, hoyoSig: Integer; BEGIN FOR hoyoSig := 1 TO N DO IF (Recorrido[hoyoActual][hoyoSig] + distActual <= distanciaMaxima) AND (hoyoSig <> hoyoActual) THEN BEGIN Comprobado[hoyoSig] := Comprobado[hoyoSig] + 1; Apilar(Camino, hoyoSig); distActual := distActual + Recorrido[hoyoActual][hoyoSig]; IF (hoyoSig = 1) AND RecorridoComprobado(Comprobado) AND (distActual < distOptima) THEN BEGIN distOptima := distActual; VaciarPila(CaminoOpt); CopiarPila(Camino, CaminoOpt); WriteLn('Solucion =', distOptima); END ELSE IF distActual < distOptima THEN ComprobarBanderas(Recorrido, hoyoSig, distanciaMaxima, distActual, distOptima, Comprobado, Camino, CaminoOpt); Comprobado[hoyoSig] := Comprobado[hoyoSig] - 1; Desapilar(Camino, aux); distActual := distActual - Recorrido[hoyoActual][hoyoSig]; END END; (* ComprobarBanderas *) PROCEDURE ImprimirCamino(VAR Camino: tPila); VAR aux: Integer; BEGIN IF NOT PilaVacia(Camino) THEN BEGIN Desapilar(Camino, aux); ImprimirCamino(Camino); Apilar(Camino, aux); Write(aux, ' ') END END; (* ImprimirCamino *) BEGIN CargarMatrizDistancias(Recorrido); WriteLn('Número de hoyos = ', N); (* Inicializamos el vector con todos los hoyos sin comprobar, salvo el 1 *) FOR v := 2 TO NMax DO Comprobado[v] := 0; Comprobado[1] := 1; IniPila(CaminoOpt); IniPila(Camino); Apilar(Camino, 1); distOptima := Infinito; ComprobarBanderas(Recorrido, 1, distanciaMaxima, 0, distOptima, Comprobado, Camino, CaminoOpt); IF distOptima = Infinito (* No se ha encontrado ninguna solución *) THEN WriteLn('No hay solución') ELSE BEGIN ImprimirCamino(CaminoOpt); WriteLn('Distancia Óptima: ', distOptima) END END. Universidad Politécnica de Madrid – E.U. Informática Página 2 2º) (3'5 Puntos) El software del proyecto "Hubble Space Telescope Key Project" genera un vector sin ordenación previa, denominado vector de distancias estelares, que recoge las distancias precisas de la mayor parte de los objetos estelares de nuestra galaxia. La participación española al proyecto no ha sido muy acertada puesto que debía encargarse de calcular la moda de dichos valores (valor esencial requerido para determinar la edad, el tamaño y el destino del universo) y debido a un problema de sincronización lo que han obtenido es un vector, denominado vector de posibles modas, que contiene 3 posibles valores para la moda. SE PIDE: Implementar un algoritmo en Pascal que indique cuál de los 3 valores alojados en el vector de posibles modas es realmente la moda de los valores alojados en el vector de distancias estelares. NOTA: No se permite la utilización de bucles. La MODA es el valor de la variable que más veces se repite, es decir, aquella cuya frecuencia absoluta es mayor. No tiene por qué ser única. Usando el esquema DyV vamos a contar el número de ocurrencias de los tres valores del vector VPM en el vector VDE. PROGRAM Ejercicio2; CONST N = 24; TYPE tIndice = 1..N; tVDE = ARRAY[tIndice] OF Integer; tVPM = ARRAY[1..3] OF Integer; VAR VDE: tVDE; VPM, VFrecuencias: tVPM; Mayor : Integer; PROCEDURE Inicializa(VAR VDE: tVDE; VAR VPM: tVPM); BEGIN (*5, 4, 5, 7, 5, 5, 10, 10, 10, 5, 2, 5, 5, 5, 10, 10, 10, 5, 10, 1, 10, 1, 10, 1*) VDE[1]:= 5; VDE[2]:= 4; VDE[3]:= 5; ..... VDE[23]:= 10; VDE[24]:= 1; VPM[1]:=5; VPM[2]:=1; VPM[3]:=10; END; PROCEDURE ObtenerFrecuenciasDyV (VDE: tVDE; inicio, fin: tIndice; VAR VFrecuencias: tVPM); VAR VFrecuenciasI, VFrecuenciasD: tVPM; medio : Integer; BEGIN IF (fin-inicio=0) THEN BEGIN VFrecuencias[1]:=0; VFrecuencias[2]:=0; VFrecuencias[3]:=0; IF VDE[inicio]=VPM[1] THEN VFrecuencias[1]:=1; IF VDE[inicio]=VPM[2] THEN VFrecuencias[2]:=1; IF VDE[inicio]=VPM[3] THEN VFrecuencias[3]:=1; END ELSE BEGIN medio := (fin+inicio) DIV 2; ObtenerFrecuenciasDyV(VDE, inicio, medio, VFrecuenciasI); ObtenerFrecuenciasDyV (VDE, medio+1, fin, VFrecuenciasD); VFrecuencias[1]:= VFrecuenciasI[1]+VFrecuenciasD[1]; VFrecuencias[2]:= VFrecuenciasI[2]+VFrecuenciasD[2]; VFrecuencias[3]:= VFrecuenciasI[3]+VFrecuenciasD[3]; END; END; Universidad Politécnica de Madrid – E.U. Informática Página 3 PROCEDURE ImprimeModas(vFrecuencias: tVPM; Mayor: Integer); BEGIN write('las modas del vector de distancias son los valores:'); IF vFrecuencias[1]=Mayor THEN write(VPM[1], ' '); IF vFrecuencias[2]=Mayor THEN write(VPM[2], ' '); IF vFrecuencias[3]=Mayor THEN write(VPM[3]); writeln(' '); writeln('Pulse cualquier tecla para finalizar'); readln; END; BEGIN Inicializa(VDE, VPM); ObtenerFrecuenciasDyV (VDE, 1, N, vFrecuencias); IF (vFrecuencias[1]>=vFrecuencias[2]) AND (vFrecuencias[1]>=vFrecuencias[3]) THEN Mayor:= vFrecuencias[1] ELSE IF (vFrecuencias[2]>=vFrecuencias[1]) AND (vFrecuencias[2]>=vFrecuencias[3]) THEN Mayor:= vFrecuencias[2] ELSE Mayor:= VFrecuencias[3]; ImprimeModas(vFrecuencias, Mayor); END. Universidad Politécnica de Madrid – E.U. Informática Página 4 3º) (3 Puntos) Dados un tablero de ajedrez en el que todas casillas tienen asignados valores aleatorios, una casilla inicial (Xi, Yi) y una casilla final (Xf, Yf). SE PIDE: Desarrollar un algoritmo en Pascal que determine si es posible que un caballo consiga llegar desde la casilla inicial a la final cumpliendo la siguiente regla: cada movimiento constará de dos pasos paseo y salto. El paseo se inicia lanzando un dado y moviendo el caballo tantas posiciones como indique el dado (el movimiento será de derecha a izquierda y de arriba hacia abajo). Una vez finalizado el paseo comenzará el salto, para lo que se obtendrá el módulo 8 del valor de la casilla en la que esté situado el caballo, que nos indicará el salto a realizar (los saltos del caballo siguen la numeración indicada en la imagen). NOTA: Si el destino de un salto no se encuentra dentro del tablero, no será considerado, pasándose de nuevo a lanzar el dado. Si el destino de un paseo no se encuentra dentro del tablero, no será considerado. En cada nivel de bactraking se genera un movimiento del caballo (un paseo más un salto). Dada la celda en la que se encuentra actualmente el caballo existen hasta 6 posibles movimientos (paseo más salto), que se corresponden con los 6 posibles valores del dado. Un movimiento es aceptable si el paseo se encuentra dentro del tablero y el destino final del caballo (bien sea sólo el destino del paseo, si el salto se sale del tablero, o el destino del salto si no se sale) no ha sido ya visitado. Si ha sido visitado previamente no es válido, ya que se entraría en un bucle infinito de llamadas recursivas. Anotar implica modificar la celda actual del caballo. Hay dos posibilidades (teniendo en cuenta que a este punto se llega después de un movimiento aceptable): que el salto esté dentro del tablero o no. Además, se deberá marcar la celda destino del caballo como visitada. Se llegará a una solución del problema si la celda actual del caballo después del movimiento actual es la celda destino del juego. Desanotar es el movimiento contrario a anotar. PROGRAM Problema3; CONST N=7; M=100; TYPE TIndice = 1..N; TCoordenadas = 1..8; TTablero = ARRAY[TIndice, TIndice] OF Integer; TVisitados = ARRAY[TIndice, TIndice] OF boolean; TDesplazamiento = ARRAY[TCoordenadas] OF -2..2; TPosicion = RECORD X,Y:TIndice; END; VAR Tablero: TTablero; Visitados : TVisitados; DespX, DespY: TDesplazamiento; solucion : boolean; Pos, CasIni, CasFin : TPosicion; {posición del caballo, casilla inicial y casilla final} PROCEDURE Inicializar_Desplazamiento(VAR DespX, DespY: TDesplazamiento); BEGIN DespX[1]:=-1; DespX[2]:=-2; DespX[3]:=-2; DespX[4]:=-1; DespX[5]:=1; DespX[6]:=2; DespX[7]:=2; DespX[8]:=1; DespY[1]:=-2; DespY[2]:=-1; DespY[3]:=1; DespY[4]:=2; DespY[5]:=2; DespY[6]:=1; DespY[7]:=-1; DespY[8]:=-2; END; Universidad Politécnica de Madrid – E.U. Informática Página 5 PROCEDURE InicializarTablero(VAR Tablero:TTablero; VAR Visitados:TVisitados); VAR i,j:integer; BEGIN FOR i:=1 TO N DO FOR j:=1 TO N DO BEGIN Tablero[i,j]:= Random(M); Visitados[i,j]:= false; END; END; PROCEDURE Ensayar(VAR Tablero:TTablero; VAR Visitados: TVisitados; Pos:TPosicion; VAR solucion:boolean); VAR NPos, NPos2: TPosicion; dado, s: Integer; BEGIN dado:=0; REPEAT dado:=dado+1; (*generamos el paseo*) IF (Pos.Y-dado)>0 THEN BEGIN (*el paseo no genera salto a la siguiente fila*) NPos.Y:=Pos.Y-dado; NPos.X:= Pos.X; END ELSE BEGIN (*el paseo genera salto a la siguiente fila*) NPos.X:=Pos.X+1; NPos.Y:= N - (dado-Pos.Y); END; IF (NPos.X<=N) THEN (*paseo aceptable, no se sale del tablero*) BEGIN s:=(Tablero[NPos.X, NPos.Y] MOD 8)+1; NPos2.X:= NPos.X + DespX[s]; NPos2.Y:= NPos.Y + DespY[s]; IF (NPos2.X >= 1)AND (NPos2.X <= N) AND (NPos2.Y >= 1) AND (NPos2.Y <= N) THEN BEGIN (*el salto es aceptable, no se sale del tablero*) NPos.X:=NPos2.X; NPos.Y:=NPos2.Y; END IF NOT Visitados[NPos.X, NPos.Y] THEN (*Con esto se evitan las posibles llamadas recursivas infinitas*) BEGIN Visitados[NPos.X, NPos.Y] := true; IF (NPos.X=CasFin.X) AND (NPos.Y=CasFin.Y) THEN solucion:=true; ELSE BEGIN Ensayar(Tablero, Visitados, NPos, solucion); IF NOT solucion THEN Visitados[NPos.X, NPos.Y]:=false; END; END END; UNTIL (dado=6) OR solucion; END; BEGIN CasIni.X:=2; CasIni.Y:=5; CasFin.X:=2; CasFin.Y:=2; Pos.X:=CasIni.X; Pos.Y:=CasIni.Y; solucion:=false; InicializarTablero(Tablero, Visitados); Visitados[CasIni.X, CasIni.Y]:=true; Inicializar_Desplazamiento(DespX,DespY); Ensayar(Tablero, Visitados, Pos, solucion); IF NOT solucion THEN write ('No hay solucion') ELSE write(‘Hay solución’) END. Universidad Politécnica de Madrid – E.U. Informática Página 6