Segundo parcial de EDA Facultad de Informática de Valencia 7 de junio de 2007 – Duración 2 horas No olvides poner el nombre. No utilices lápiz ni tinta roja. Se piden siempre soluciones eficientes. Pregunta 1 (3 puntos) Dispones de la siguiente clase en C++ para representar un grafo dirigido mediante listas de adyacencia. struct a r i s t a { int d e s t i n o ; arista ∗ sig ; }; class grafo { public : i n t numV ; // v é r t i c e s de 0 a numV−1 a r i s t a ∗ ∗ l a ; // l a e s un v e c t o r de tamaño numV, t i p o a r i s t a ∗ g r a f o ( i n t nVert ) ; // c o n s t r u c t o r ... g r a f o ∗ a r b o l b f s ( i n t r a i z ) ; // EJERCICIO DE EXAMEN }; g r a f o : : g r a f o ( i n t nVert ) { numV = nVert ; l a = new a r i s t a ∗ [numV ] ; f o r ( i n t u = 0 ; u<numV ; u++) l a [ u ] = 0 ; } Se pide implementar el método arbol_bfs(int raiz) en la clase grafo que recibe como argumento un vértice del grafo y devuelve otro grafo que tiene los mismos vértices y sólamente aquellas aristas del árbol del recorrido primero en anchura que empieza en el vértice raı́z y tiene como aristas las que van alcanzando vértices no visitados, tal y como se muestra en el ejemplo: 0 1 2 3 4 0 1 2 3 5 Se supone implementada la siguiente clase para trabajar con colas de enteros: class cola { // a t r i b u t o s y /o métodos v a r i o s public : void e n c o l a r ( i n t n ) ; bool d e s e n c o l a r ( i n t &n ) ; cola () ; ˜ cola () ; bool v a c i a ( ) ; }; 5 4 Solución g r a f o ∗ g r a f o : : a r b o l b f s ( int r a i z ) { bool ∗ v i s i t a d o = new bool [ numV ] ; f o r ( i n t i = 0 ; i <numV ; i ++) v i s i t a d o [ i ] = f a l s e ; g r a f o ∗ r e s u l = new g r a f o (numV) ; cola c ; c . encolar ( raiz ) ; v i s i t a d o [ r a i z ] = true ; int u ; while ( ! c . v a c i a ( ) ) { c . desencolar (u) ; f o r ( a r i s t a ∗ r=l a [ u ] ; r ! = 0 ; r=r−>s i g ) { i f ( ! v i s i t a d o [ r−>d e s t i n o ] ) { c . e n c o l a r ( r−>d e s t i n o ) ; v i s i t a d o [ r−>d e s t i n o ] = true ; a r i s t a ∗ nueva = new a r i s t a ; nueva−>d e s t i n o = r−>d e s t i n o ; nueva−>s i g = r e s u l −>l a [ u ] ; r e s u l −>l a [ u ] = nueva ; } } } // l a c o l a v a c i a s e d e s t r u y e con su d e s t r u c t o r delete [ ] v i s i t a d o ; return r e s u l ; } Pregunta 2 (2 puntos) Se quiere calcular el flujo máximo de un grafo utilizando el algoritmo de Ford-Fulkerson. Para ello, se dispone de una función ya implementada que obtiene un camino de aumento en el grafo residual representado mediante una matriz de adyacencia: i n t b u s c a r c a m i n o ( f l o a t ∗ ∗m, i n t numVert , i n t desde , i n t hasta , i n t ∗ camino ) ; Esta función recibe el grafo residual en la matriz m cuadrada de lado numV ert, ası́ como los vértices origen y destino del camino a buscar. Busca un camino que pase por aristas de peso positivo y devuelve la longitud de dicho camino, dejando la secuencia de vértices en el vector camino. Se pide implementar el algoritmo de Ford-Fulkerson: f l o a t f o r d f u l k e r s o n ( f l o a t ∗ ∗m, i n t numVert , i n t f u e n t e , i n t sumidero ) ; que recibe la red de flujo o grafo representada mediante matriz de adyacencia (la ausencia de aristas se denota con capacidad cero), ası́ como los ı́ndices de los vértices fuente y sumidero. Solución f l o a t f o r d f u l k e r s o n ( f l o a t ∗ ∗m, i n t numVert , i n t f u e n t e , i n t sumidero ) { f l o a t maxflow = 0 . 0 ; i n t ∗ camino = new in t [ numVert ] ; while ( ( l o n g i t u d=b u s c a r c a m i n o (m, numVert , f u e n t e , sumidero , camino ) ) >0) { // c a l c u l a r d e l t a : d e l t a = m[ camino [ 0 ] ] [ camino [ 1 ] ] ; f o r ( i n t i =1; i <l o n g i t u d ; i ++) { i f ( d e l t a > m[ camino [ i ] ] [ camino [ i + 1 ] ] ) d e l t a = m[ camino [ i ] ] [ camino [ i + 1 ] ] ; } // a c t u a l i z a r e l g r a f o r e s i d u a l con d e l t a : f o r ( i n t i =1; i <l o n g i t u d ; i ++) { m[ camino [ i ] ] [ camino [ i +1]] −= d e l t a ; m[ camino [ i + 1 ] ] [ camino [ i ] ] + = d e l t a ; } maxflow += d e l t a ; } delete [ ] camino ; return maxflow ; } Pregunta 3 (3 puntos) Hemos entrado a robar en un almacén lleno de cajas del mismo tamaño. Queremos llevarnos la mercancı́a con una carretilla elevadora o Fenwick que sólo puede cargar una pila de cajas. Cada caja viene etiquetada con su peso y con el peso que puede soportar encima (si nos pasamos, se aplasta y pierde su valor). Se pide que implementes un algoritmo que, utilizando la técnica de búsqueda con retroceso, calcule formas de apilar K cajas sin que ninguna de las mismas se aplaste por las que tiene encima. Como ayuda te sugerimos la siguiente cabecera: void c a l c u l a r ( i n t ∗ peso , i n t ∗ s o p o r t a , bool ∗ puesta , i n t numCajas , i n t ∗ s o l u c i o n , i n t l o n g s o l , i n t K) ; donde peso, soporta y puesta son vectores de talla numCajas, mientras que solucion es un vector de talla K. Debes utilizar la siguiente función (que no hay que implementar) para que se procesen las distintas soluciones encontradas: void p r o c e s a r s o l u c i o n ( i n t ∗ peso , i n t ∗ s o p o r t a , i n t numCajas , i n t ∗ s o l u c i o n , i n t K) ; Solución bool f a c t i b l e ( i n t c a j a , i n t ∗ peso , i n t ∗ s o p o r t a , bool ∗ puesta , i n t ∗ s o l u c i o n , i n t l o n g s o l ) { i f ( p u e s t a [ c a j a ] ) return f a l s e ; i n t acumulado = p e s o [ c a j a ] ; f o r ( i n t i=l o n g s o l − 1 ; i >=0; −− i ) { i f ( acumulado>s o p o r t a [ s o l u c i o n [ i ] ] ) return f a l s e ; acumulado += p e s o [ s o l u c i o n [ i ] ] ; } return true ; } void c a l c u l a r ( i n t ∗ peso , i n t ∗ s o p o r t a , bool ∗ puesta , i n t numCajas , i n t ∗ s o l u c i o n , i n t l o n g s o l , i n t K) { i f ( l o n g s o l == K) { p r o c e s a r s o l u c i o n ( peso , s o p o r t a , numCajas , s o l u c i o n ,K) ; } e l s e { // buscamos l a forma de meter OTRA c a j a : f o r ( i n t c a j a =0; c a j a <numCajas ; c a j a++) i f ( f a c t i b l e ( c a j a , peso , s o p o r t a , puesta , s o l u c i o n , l o n g s o l ) ) { solucion [ longsol ] = caja ; p u e s t a [ c a j a ] = true ; c a l c u l a r ( peso , s o p o r t a , puesta , numCajas , s o l u c i o n , l o n g s o l +1,K) ; puesta [ caja ] = false ; } } } // l l a m a d a i n i c i a l ( no s e p e dı́ a en e l examen ) void e j e r c i c i o ( i n t ∗ peso , i n t ∗ s o p o r t a , i n t numCajas , i n t K) { p u e s t a = new bool [ numCajas ] ; f o r ( i n t i =0; i <numCajas ; i ++) puesta [ i ] = false ; s o l u c i o n=new in t [K ] ; c a l c u l a r ( peso , s o p o r t a , puesta , numCajas , s o l u c i o n , 0 ,K) ; delete [ ] p u e s t a ; delete [ ] s o l u c i o n ; } Otra solución También podemos simplificar la comprobación de factible si ponemos las cajas al revés a como se ponen en el Fenwick: desde la superior hacia abajo. En tal caso podemos llevar el peso acumulado de todas las cajas como un argumento adicional: void c a l c u l a r ( i n t ∗ peso , i n t ∗ s o p o r t a , bool ∗ puesta , i n t numCajas , i n t ∗ s o l u c i o n , i n t l o n g s o l , i n t K, i n t pesoAcumulado ) { i f ( l o n g s o l == K) { p r o c e s a r s o l u c i o n ( peso , s o p o r t a , numCajas , s o l u c i o n ,K) ; } e l s e { // buscamos l a forma de meter OTRA c a j a : f o r ( i n t c a j a = 0 ; c a j a <numCajas; ++ c a j a ) i f ( ! p u e s t a [ c a j a ] && pesoAcumulado < s o p o r t a [ c a j a ] ) { s o l u c i o n [ K−1− l o n g s o l ] = c a j a ; p u e s t a [ c a j a ] = true ; c a l c u l a r ( peso , s o p o r t a , puesta , numCajas , s o l u c i o n , l o n g s o l +1,K, pesoAcumulado+p e s o [ c a j a ] ) ; puesta [ caja ] = false ; } } } // l l a m a d a i n i c i a l ( no s e p e dı́ a en e l examen ) void e j e r c i c i o ( i n t ∗ peso , i n t ∗ s o p o r t a , i n t numCajas , i n t K) { p u e s t a = new bool [ numCajas ] ; f o r ( i n t i =0; i <numCajas ; i ++) puesta [ i ] = false ; s o l u c i o n=new in t [K ] ; c a l c u l a r ( peso , s o p o r t a , puesta , numCajas , s o l u c i o n , 0 ,K, 0 ) ; delete [ ] p u e s t a ; delete [ ] s o l u c i o n ; } Pregunta 4 (2 puntos) Dada la siguiente implementación de un árbol n-ario basado en la representación hijo izquierdo y hermano derecho: struct nodo { // c l a s e a u x i l i a r int v a l o r ; nodo ∗ h i j o , ∗ hno ; }; c l a s s a r b o l { // á r b o l b i n a r i o de b ú s q u e d a public : nodo ∗ r a i z ; arbol () ; ˜ arbol () ; }; Se pide implementar: Un método contar_aridad_par que determine cuántos nodos tienen un número par de hijos. Solución i n t nodo : : c o n t a r a r i d a d p a r ( ) { i n t h i j o s =0 , r e s u l t a d o = 0 ; f o r ( nodo ∗ r=h i j o ; r ! = 0 ; r=r−>hno ) { r e s u l t a d o += r−>c o n t a r a r i d a d p a r ( ) ; h i j o s ++; } i f ( h i j o s % 2 == 0) r e s u l t a d o ++; return r e s u l t a d o ; } int a r b o l : : c o n t a r a r i d a d p a r ( ) { return ( r a i z ==0) ? 0 : r a i z −>c o n t a r a r i d a d p a r ( ) ; } Y también debemos añadir las correspondientes declaraciones de los dos métodos en sus respectivas clases: struct nodo { // c l a s e a u x i l i a r int v a l o r ; nodo ∗ h i j o , ∗ hno ; int c o n t a r a r i d a d p a r ( ) ; }; c l a s s a r b o l { // á r b o l b i n a r i o de b ú s q u e d a public : nodo ∗ r a i z ; arbol () ; ˜ arbol () ; int c o n t a r a r i d a d p a r ( ) ; };