Considere 3 jarros de água 1,2,3 com capacidades 12,8,5 respectivamente. A água total retida pelos 3 jarros deve ser de 12 unidades. Podemos esvaziar a jarra x na jarra y (xEy) ou encher a jarra y da jarra x (xFy). Com essas considerações em mente, dados um estado inicial e um estado objetivo, como podemos saber se o estado objetivo é alcançável em etapas finitas sem realmente executar o algoritmo?
A equação x+y+z = 12 tem 53 soluções, ou seja, 2756 pares possíveis de estados inicial e final. Inspecionar todos esses pares para acessibilidade individualmente, mesmo em um computador, é uma loucura, como o estado objetivo (0,7,5) tem 12 estados iniciais para os quais é alcançável. Ou ainda não pensei em um bom algoritmo. De qualquer forma, o número é muito grande e eu quero alguma 'regra de ouro' para identificar os estados alcançáveis. Qualquer ajuda ou ideia seria apreciada.
Vamos identificar exclusivamente um estado de jarro como a quantidade de água em todos os 3 jarros com esta pequena estrutura de dados (uma matriz de 3 inteiros). Para este exercício, codificarei em C/C++, mas pode ser facilmente adaptado para qualquer linguagem de programação .
Assim, por exemplo, se o primeiro jarro tiver 4 unidades de água, o segundo jarro tiver 5 e o terceiro jarro tiver 3. Então podemos ter um JugState acima de,
{4,5,3}
por exemplo.Mas dado que os jarros têm uma capacidade fixa de 12, 8 e 5, respectivamente. Podemos facilmente ter uma representação inteira do estado do jarro. A configuração {4,5,3} acima pode ser facilmente representada com o número
453
. Uma configuração de jarro{11,0,1}
pode ser representada como1101
. Não precisa ser uma codificação decimal, mas facilita a depuração.Podemos ter funções auxiliares que convertem de JugState para "índice de estado do jarro".
Agora podemos definir uma função que move a água de um jarro para outro. Esta função está ciente das capacidades de 12,8 e 5 unidades de cada jarro. Toma como parâmetro o "índice de estado do jarro" inicial, e dois parâmetros para indicar de onde a água está sendo movida (por exemplo, 0,1,2 índices do JugState). Por exemplo, se você invocá-lo,
moveWater(453, 0, 1)
basicamente está dizendo "mova a água do primeiro jarro para o segundo jarro". Deve retornar neste caso,183
pois apenas 3 unidades do primeiro jarro podem ser despejadas no segundo.Agora podemos pensar neste problema como um gráfico. Dado qualquer estado como
{4,5,3}
(índice == 453) e queremos testar se é possível fazer a transição para{7,1,4}
(índice == 714). Em qualquer estado, existem 6 transições válidas que podem ser feitas. (ou seja, mova a água de jug0->jug1, jug0->jug2, ..., jug2->jug0). Portanto, podemos apenas usar aset
para acompanhar quais índices de nó visitamos, para que não recursemos indefinidamente. A parte legal dessa travessia de árvore é que não precisamos construir uma árvore real.Agora vamos apresentar 2 funções auxiliares. Uma função para nos ajudar a construir esse conjunto inicial de 53 estados de jarros válidos com 12 unidades de água. Esta função está ciente dos limites de 12/8/5 nos jarros. Mas você pode passar qualquer valor do estado de destino que desejar.
E uma função auxiliar para fazer uma representação em string do estado de um jarro:
Agora temos o suficiente para codificar um teste para todas as 2.756 combinações de transições para as 12 unidades de configurações de água.
Execute nosso código (aqui está um trecho)...
Você pode obter a listagem completa aqui:
https://github.com/jselbie/threejugproblem/blob/main/main.cpp