Problema dos Potes de Vinho

Considere que temos três potes com capacidades de 8, 5 e 3 litros, respectivamente, os quais não possuem qualquer marcação. O maior deles esta completamente cheio enquanto que os outros dois estão vazios. Estamos interessados em dividir o vinho em duas porções iguais de 4 litros, tarefa  esta que pode ser realizada por transvasos sucessivos de um pote no outro. Qual o menor número de transvasos necessários para completar a divisão?

Sejam c1 = 8, c2 = 5 e c3 = 3 respectivamente as capaciades dos três potes p1, p2 e p3. Então, um possível modelo para este problema é definir o grafo G(V,A) como:

V = { (q1, q2, q3) | q1, q2 e q3 representam as quantidades de vinho dos três potes, sendo que: 

A = { (e1,e2) | o estado e2 é alcançado partindo-se do estado e1 por meio de um único transvaso de um pote para outro, cuja; o transvaso é completado quando o pote de origem é esvaziado ou quando o pote de destino é enchido}

Sejam px e py respectivamente os pote de origem e de destino do transvaso. Segundo o descrito a definição das arestas acima, um transvaso é possível se o pote de origem não estiver vazio (qx > 0) e o pote de destino não estiver cheio (qy < cy). A quantidade a ser transvasada é dada pelo mínimo entre o que tem no pote de origem (qx) e o que ainda cabe no pote de destino (cy-qy). Como resultado de um transvaso, a quantidade do pote px é descrescida em t unidades, enquanto que a quantidade do pote py é acrescida de t unidades.

pré-condição ação pós-condição
qx > 0 e (qy < cy) e (x ¹ y) transvaso de t = mínimoEntre(qx,cy-qy)
litros de px para py
q'x = qx - t
q'y = qy + t

Considerando esta regra e escrevendo um algoritmo de busca em profundidade, podemos obter todos os estados alcançáveis partindo-se do estado inicial (8,0,0) até chegar ao nodo de destino (4,4,0). O algoritmo abaixo exemplifica esta busca:

grafo := Digrafo new.
capacidades := #(8 5 3).
self buscaEmProfundidade3PotesDe: #(8 0 0) para: #(4 4 0) jaVisitados: Set new.
buscaEmProfundidade3PotesDe: origem para: destino jaVisitados: visitados

	|qx qy cy t meio|

	origem = destino  ifTrue: [
	    ^self
	].

	visitados add: origem.
	1 to: 3 do: [:x|
	   1 to: 3 do: [:y|
		cy := capacidades at: y.
		qx := origem at: x.
		qy := origem at: y.
		(x ~= y and: [ qx > 0 and: [qy < cy]]) ifTrue: [
		    t := qx min: cy - qy.
              meio := origem copy.
              meio at: x put: qx - t;
                   at: y put: qy + t.
              grafo conecta: origem a: meio.
              (visitados includes: meio) ifFalse: [
                   self buscaEmProfundidade3PotesDe: meio para: destino 
						jaVisitados: visitados.
              ].
		]
	    ]
	].

O grafo resultante desta busca é apresentado na figura que se segue.

Diferentemente do que acontece com o problema do canibais e dos missionários, neste caso não é tão fácil visualizar a solução do problema dado que o grafo é bem mais emaranhado.

Para encontrar a melhor solução, pode-se obter o caminho de custo mínimo que vai do nodo (8,0,0) até o (4,4,0), considerando que cada aresta tem peso 1. Alternativamente pode-se modificar o algoritmo apresentado acima para que ela vá guardando a melhor solução à medida que as soluções vão sendo encontradas. O algoritmo seguinte exemplifica esta alternativa:

grafo := Digrafo new.
capacidades := #(8 5 3).
melhorCaminho := nil.
self buscaEmProfundidade3PotesDe: #(8 0 0) para: #(4 4 0) 
			caminho: OrderedCollection new.
self realcaMelhorCaminho.
buscaEmProfundidade3PotesDe: origem para: destino caminho: caminho

	|qx qy cy t meio|

	origem = destino  ifTrue: [
	    (melhorCaminho isNil or:
		[caminho size + 1 < melhorCaminho size])ifTrue: [
           melhorCaminho := caminho copy.
           melhorCaminho addLast: destino.
	    ].
     ^self
	].
	(caminho includes: origem) ifTrue: [
         ^self
	].

	caminho addLast: origem.
	1 to: 3 do: [:x|
	    1 to: 3 do: [:y|
             cy := capacidades at: y.
             qx := origem at: x.
             qy := origem at: y.
             (x ~= y and: [ qx > 0 and: [qy < cy]]) ifTrue: [
                 t := qx min: cy - qy.
                 meio := origem copy.
                 meio at: x put: qx - t;
                      at: y put: qy + t.
                 grafo conecta: origem a: meio.
                 self buscaEmProfundidade3PotesDe: meio para: destino 
				caminho: caminho.
            ]
	    ]
	].
	caminho removeLast.
realcaMelhorCaminho

	1 to: melhorCaminho size - 1 do: [:indice|
	    (grafo arestaDe: (melhorCaminho at: indice) para: 
			(melhorCaminho at: indice + 1))
              cor: Vermelho;
              espessura: 2.
	].

	grafo arestas do: [:aresta|
	     aresta cor = Vermelho ifFalse: [
             grafo removeAresta: aresta
	     ].
	].
	grafo nodos do: [:nodo|
	    ((grafo grauDeEmissao: nodo) = 0 and: 
			[(grafo grauDeRecepcao: nodo) = 0])ifTrue: [
             grafo removeNodo: nodo
       ].
	].

O grafo resultante da aplicação deste algoritmo, que representa a melhor solução para o problema, é apresentado na figura que se segue:

Pode-se também fazer uma busca em largura, utilizando um algoritmo similar ao descrito abaixo:
grafo := Digrafo new.
capacidades := #(8 5 3).
self buscaEmLargura3PotesDe: #(8 0 0) para: #(4 4 0).
buscaEmLargura3PotesDe: origem para: destino

        |qx qy cy t meio aVisitar proximo|

        aVisitar := OrderedCollection with: origem.

        [aVisitar isEmpty] whileFalse: [
            proximo := aVisitar removeFirst.
            1 to: 3 do: [:x|
                1 to: 3 do: [:y|
                    cy := capacidades at: y.
                    qx := proximo at: x.
                    qy := proximo at: y.
                    (x ~= y and: [ qx > 0 and: [qy < cy]]) ifTrue: [
                        t := qx min: cy - qy.
                        meio := proximo copy.
                        meio at: x put: qx - t;
                                 at: y put: qy + t.
                        meio = destino ifTrue: [
                                grafo conecta: proximo a: meio.
                        ] ifFalse: [
                                (grafo existeNodo: meio)  ifFalse: [
                                    grafo conecta: proximo a: meio.
                                    aVisitar addLast: meio
                                ]
                        ].
                    ]
                ]
            ]
        ].

        ^false

O resultado da busca em largura é apresentado na figura que se segue:


Voltar