Tenemos una entrada de n ciudades y m posibles carreteras a construir entre ellas. Cada carretara nos aporta una cantidad
def Solve(streetName, streets, cityTake, cities, funds):
if len(streets) == 0:
return [], 0
currentStreet = streetName[0]
nTakePath, nTake = Solve(streetName[1:], streets[1:], , cities, funds)
if not cityTake[cStreet[0]]:
funds += cities[cStreet[0]]
cityTake[cStreet[0]] = True
if not cityCopy[cStreet[1]]:
funds += cities[cStreet[1]]
cityTake[cStreet[1]] = True
gain = streets[0] - funds
if gain >= 0:
funds = 0
else:
funds -= streets[0]
TakePath, Take = Solve(streetName[1:], streets[1:], cityCopy, cities, funds)
TakePath.append(cStreet)
Take += gain
if Take > nTake:
return TakePath, Take
else:
return nTakePath, nTake
La idea se reduce a dado un conjunto de aristas(calles) decidir cuales se toman y cuales se desechan. En cada paso del algoritmo se calcula la ganancia si se decidiera tomar o desechar una arista(calle) teniendo en cuenta la pérdida generada por las ciudades, la combinación que más ganancias genera es dada como solución. La complejidad de este algoritmo es
La idea principal es cómo modelamos el grafo de forma tal que quede como una red de flujo. Partiendo de la idea que queremos encontrar la máxima ganancia que se puede transmitir entre los vértices de fuente y destino.
Viéndolo de esta manera nos damos cuenta que lo que queremos transmitir por nuestra red es el dinero y así quedarnos con el máximo posible.
En este momento encontramos el primer problema, pues las carreteras suman y las ciudades restan dinero. No supimos modelar ese concepto con una red de flujo.
Es fácil notar que la idea anterior es equivalente a tener todo el dinero otorgado por las carreteras y solo quedaría restar el dinero de las ciudades que conectamos y de las calles que no construimos.
Después de muchos intentos no llegamos muy lejos con estas ideas.
Siguiendo la línea de pensamiento inicial donde queríamos obtener la máxima ganancia de una red de flujo donde se transmite dinero, es obvio que la capacidad de las aristas fuese dinero.
De esta forma llegamos a la idea de que las carreteras eran como fuentes que daban dinero y las ciudades consumidores de ese dinero.
No es muy difícil modelar las carrteras como fuente pues consideramos las carreteras como nodos conectados a una fuente y las capacidades de esas aristas son las respectivas cantidades de dinero que obtenemos por construirlas.
Ahora solo era necesario modelar las ciudades como "consumidores" de dinero cosa que no encotramos nunca, pero estaba claro que las carreteras tenían que estar conectadas a las ciudades que unían.
Teniendo este modelado pensamos en poner la capacidad de esas aristas como el dinero que restan las ciudades al construir una carretera que llega a ella. Esto no tiene sentido pues si existían dos carreteras que llegaban a la misma ciudad duplicábamos su costo.
A pesar de ser una idea equivocada nos ayudó mucho porque interpretamos los costos de las ciudades como aristas que limitan el flujo del dinero y así la ganancia sería la capacidad de las aristas que van de la fuente a las carreteras que no pudimos saturar, cosa que coincide con la fórmula inicial:
siendo A el conjunto de las carreteras construidas y C el conjunto de las ciudades que están unidas por esas carreteras.
Teniendo esto ya es muy fácil ver el flujo del dinero. Sale de la fuente hacia cada una de las carreteras con su costo respectivo y llega de las ciudades al destino con el costo respectivo de cada una de ellas.
El costo de las aristas que conectan a las carreteras con las ciudades es infinito. No tiene sentido restringir el dinero que pasa por ellas.
Para resolver nuestro problema hallamos el valor del flujo máximo en la red de flujo creada anteriormente y tenemos que el dinero que pasa por las manos de Tito es el total que brinda el gobierno por construir cada carretara menos el valor del flujo del máximo. Las carreteras que tenemos que construir son las que petenecen al corte mínimo.
El grafo
La capacidad de una red de flujo es una función
La función de capacidad para esta red
Sabemos que el valor del flujo es igual a la capacidad del corte mínimo y que las aristas que unen a los vértices que pertenecen a
Sea
Si dado el corte mínimo existe un nodo carretera
Si dado el corte mínimo existe un nodo ciudad
Por tanto la respuesta final será todo
def SolveFordFulkerson(graph):
graph, cities, streetName = GraphBuilder(graph)
g = Graph(graph)
source = 0
sink = np.shape(g.graph)[0]-1
maxf = g.ford_fulkerson(source, sink)
solve = g.graph
streets = []
cST = DFS(np.array(g.graph), 0, [False for i in range(np.shape(g.graph)[0])])
for i in range(len(streetName)):
if cST[i + 1]:
streets.append(streetName[i])
return streets
El método Graph se encarga de crear un grafo con la estructura anteriormente mencionada. La clase Graph contiene una implementación del algoritmo Ford-Fulkerson para hallar flujo máximo. Se calcula el flujo máximo de este grafo. Luego se realiza un DFS desde la fuente sobre la red residual para obtener la partición del corte mínimo que contiene a S. Los vértices carretera pertenecientes a este conjunto serán nuestra solución.