Problema dos canibais e dos missionários

Três canibais e três missionários estão viajando juntos e chegam à margem de um rio. Eles desejam atravessar para a outra margem para, desta forma, continuar a viagem. O único meio de transporte disponível é um barco que comporta no máximo duas pessoas. Há uma outra dificuldade: em nenhum momento o número de canibais pode ser superior ao número de missionários pois desta forma os missionários estariam em grande perigo de vida. Como administrar a travessia?

Para resolver este problema, considere as situações (ou estados) no que se refere ao número de canibais e missionários que estão numa das duas margens, sempre considerando que o barco está aportado, ou seja, antes do início de uma travessia ou quando ela foi completada. Podemos representar cada um destes estados por uma tripla de valores (c,m,b), onde c e m representam o número de canibais e de missionários, respectivamente, que estão na margem b onde está o barco.

Uma tripla como (2, 2, esquerda) significa que temos dois canibais e dois missionários na margem esquerda, e que o barco está nesta margem. Apesar de não estar explicitamente dito, é fácil saber que neste instante temos 1 canibal e 1 missionário na margem direita (complemento em relação ao par (3,3)).


Um modelo para este problema é dado, então, pelo grafo G(V,A):

V = { (c,m,b) | c e m representam o número de canibais e de missionários na margem b do rio, estando o barco nesta margem} 

A = { (ex,ey) | ex = (cx,mx,bx) é o estado na margem onde o barco está antes da travessia ser iniciada, e ey = (cy,my,by) é o estado na margem oposta após o barco completar a travessia}

Como o barco comporta no máximo 2 pessoas, há 5 possíveis situações quanto aos passageiros do barco em cada travessia:

A tabela que se segue descreve as restrições par que uma travessia possa ocorrer, considerando x e y com sendo as margens de origem e de destino de uma travessia do barco, respectivamente.

regra pré-condição ação pós-condição
1 cx ³ t
(3 - mx) = 0 ou (3 - mx) - (3 - cx³  t
1 £ t £ 2
travessia de t canibais da margem x para y cy = (3 - cx+ t
my = (3 - mx)
2 mx ³ t
mx - t = 0 ou (mx - t) ³ cx
(3 - cx) = 0 ou (3 - cx) - (3 - mx£ t
1 £ t £ 2
travessia de t missionários da margem x para y my = (3 - mx+ t
cy = (3 - cx)
3 cx ³ 1
mx ³ 1
(3 - cx) = 0 ou (3 - cx) £ (3 - mx)
travessia de 1 canibal e de 1 missionário da margem x para y cy = (3 - cx+ 1
my = (3 - mx+ 1

Regra 1

Para que t canibais, 1 £ t £ 2, atravessem sozinhos o rio, é preciso que haja t canibais na margem de origem (cx ³ t). Também é preciso que ao chegarem estes t canibais na margem de destino, o número total de canibais não exceda o número de missionários. Isto é obtido em duas situações: se não houver nenhum missionário na margem oposta antes da travessia do barco, ou seja,  (3 - mx) = 0; haja pelo menos t missionários a mais do que canibais na margem oposta antes da travessia do barco, ou seja, (3 - mx ) - (3 - cx)  ³ t.

Regra 2

Para que t missionários, 1 £ t £ 2, atravessem sozinhos o rio, é preciso que haja t missionários na margem de origem (mx ³ t). Também é preciso que não restem mais canibais do que missionários na margem de origem, ou seja, que (mx - t) ³ cx, ou que o número de missionários restantes seja zero (mx - t = 0). Quanto à margem de destino, ou ela não tem nenhum canibal antes do início da travessia (3 - cx) = 0, ou o número de canibais não exceda o número de missionários em mais do que t unidades ((3 - cx) - (3 - mx£ t).

Regra 3

Para que 1 canibal e 1 missionário atravessem o rio, é preciso que haja pelo menos 1 canibal  (cx ³ 1) e 1 missionário (mx ³ 1) na margem de origem. Também é preciso garantir que o número de canibais não exceda o número de missionários na margem de destino, ou seja, que não haja nenhum canibal antes da travessia ((3 - cx) = 0), ou que o número de canibais não seja superior ao número de missionários ((3 - cx) £ (3 - mx)).


Um algoritmo de busca em profundidade que implementa estas regras é dado na tabela que se segue:

grafo := Grafo new.
self buscaEmProfundidadeDe: #(3 3 esquerda) para: #(3 3 direita) 
			jaVisitados: Set new.
buscaEmProfundidadeDe: origem para: destino jaVisitados: visitados

	|cx mx cy my|

	origem = destino  ifTrue: [
	  ^self
	].

	visitados add: origem.
	cx := origem at: 1.
	mx := origem at: 2.
	cy := 3 - cx.
	my := 3 - mx.
	1 to: 2 do: [:t|
	  (cx >= t and: [my = 0 or: [my - cy >= t]]) ifTrue: [
	     self travessiaDeCanibais: t de: origem para: destino 
			jaVisitados: visitados
	  ].
	  (mx >= t and: [(mx - t = 0) | (mx - t >= cx) 
			and: [(cy = 0) | (cy - my <= t)]]) ifTrue: [
	     self travessiaDeMissionarios: t de: origem para: destino 
			jaVisitados: visitados
	  ].
	].
	(cx >= 1 and: [mx >= 1 and: [(cy = 0) | (cy <= my)]]) ifTrue: [
	   self travessiaDeUmCanibalUmMissionarioDe: origem para: destino 
			jaVisitados: visitados
	]
travessiaDeCanibais: t de: origem para: destino 
		jaVisitados: visitados

        |cx mx cy my meio margemDoBarco|

        cx := origem at: 1.
        mx := origem at: 2.
        cy := 3 - cx.
        my := 3 - mx.

        meio := origem copy.
        meio at: 1 put: cy + t.
        meio at: 2 put: my.
        (origem at: 3) = #direita ifTrue: [
               meio at: 3 put: #esquerda
        ] ifFalse: [
               meio at: 3 put: #direita
        ].

        grafo conecta: origem a: meio rotulo: (Array with: t with: 0).
        (visitados includes: meio) ifFalse: [
                self buscaEmProfundidadeDe: meio para: destino  
			jaVisitados: visitados.
        ]
travessiaDeMissionarios: t de: origem para: destino 
		jaVisitados: visitados

        |cx mx cy my meio margemDoBarco|

        cx := origem at: 1.
        mx := origem at: 2.
        cy := 3 - cx.
        my := 3 - mx.

        meio := origem copy.
        meio at: 1 put: cy.
        meio at: 2 put: my + t.
        (origem at: 3) = #direita ifTrue: [
               meio at: 3 put: #esquerda
        ] ifFalse: [
               meio at: 3 put: #direita
        ].

        grafo conecta: origem a: meio rotulo: (Array with: 0 with: t).
        (visitados includes: meio) ifFalse: [
                self buscaEmProfundidadeDe: meio para: destino  
			jaVisitados: visitados.
        ]
travessiaDeUmCanibalUmMissionarioDe: origem para: destino 
		jaVisitados: visitados

        |cx mx cy my meio margemDoBarco|

        cx := origem at: 1.
        mx := origem at: 2.
        cy := 3 - cx.
        my := 3 - mx.

        meio := origem copy.
        meio at: 1 put: cy + 1.
        meio at: 2 put: my + 1.
        (origem at: 3) = #direita ifTrue: [
               meio at: 3 put: #esquerda
        ] ifFalse: [
               meio at: 3 put: #direita
        ].

        grafo conecta: origem a: meio rotulo: (Array with: 1 with: 1).
        (visitados includes: meio) ifFalse: [
                self buscaEmProfundidadeDe: meio para: destino  
				jaVisitados: visitados.
        ]

O grafo obtido por este algoritmo é apresentado abaixo:

Por este grafo é possível observar que  há quatro possíveis  seqüências de travessias para efetuar o translado dos canibais e dos missionários e que em todas elas são necessárias no mínimo 11 travessias do barco .


Voltar