算法小程序:倒水問題

問題描述

有三個容積分別爲3,5,8升的水桶,其中容積爲8升的水桶中裝了水,容積爲3,5的水桶爲空。水桶沒有刻度尺,現在需要將水桶中的8升水等分成2分,每份都是4升水,該怎麼分。總共有多少種分方法

思路

水桶分別爲3L,5L,8L,而且沒有刻度尺衡量倒多少水,所有倒水要麼被裝的水桶倒滿,要麼倒水的桶倒完。
我們可以給每個桶標記一種狀態,初始狀態就是8L水桶滿,其餘兩個水桶爲空,每倒一次,兩個桶的狀態會改變,動態遞歸算法,把每一種水桶可以倒的情況都嘗試一遍,直到滿足兩個4L水爲止。但是,這樣有可能會出現重複的情況,比如8L水桶倒向3L水桶,3L水桶又倒回8L水桶,這樣狀態就重複了,永遠都倒不完,所有,我們把每步走的狀態都放到棧中,如果出現重複狀態,立即結束當前倒水

代碼實例

下面使用java實現的

import java.util.ArrayList;
import java.util.List;

public class 倒水問題 {

	/*
	 * 有三個容積分別爲3,5,8升的水桶,其中容積爲8升的水桶中裝了水,容積爲3,5的水桶爲空。
	 * 水桶沒有刻度尺,現在需要將水桶中的8升水等分成2分,每份都是4升水,該怎麼分。
	 * 總共有多少種分方法
	 */
	
	public static void main(String[] args) {
		Action a = new Action();
		a.bucket[0]=8;
		a.bucket[1]=0;
		a.bucket[2]=0;
		fun(a);
		System.out.println("共有:"+num+"種");

	}
	//水桶規則
	public static final int[]rule = {8,5,3};
	//記錄有多少種分法
	public static int num=0;
	//分水執行的動作,包括水桶狀態,分水的情況
	//----------------------------------------------------------------
	//倒水情況
	public static void fun(Action action){
		//出口,當平分,或者回路時退出
		for (int[] r : action.rout) {
			if(isequal(r, action.bucket))	//如果兩個水桶水相同,即走到了迴路,停止
					return;
		}
		//添加新路徑
		{
			int []t = new int[3];
			for (int i = 0; i < action.bucket.length; i++) {
				t[i]=action.bucket[i];
			}
			action.rout.add(t);
		}
		//判斷平分
		if(action.bucket[0]==4 && action.bucket[1]==4){
			for (int[] r : action.rout) {
				System.out.print(r[0]+":"+r[1]+":"+r[2]+":"+"--->");
			}
			System.out.println();
			num++;
			return;
		}
		//---------
		//6種倒法
		if(canPour(0, 1, action))	//1倒2
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(0, 1, action);	
			fun(action);
			action=tem;
		}
		if(canPour(0, 2, action))	//1倒3
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(0, 2, action);
			fun(action);
			action=tem;
		}
		if(canPour(1, 0, action))	//2倒1
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(1, 0, action);
			fun(action);
			action=tem;
		}
		if(canPour(1, 2, action))	//2倒3
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(1, 2, action);
			fun(action);
			action=tem;
		}
		if(canPour(2, 0, action))	//3倒1
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(2, 0, action);
			fun(action);
			action=tem;
		}
		if(canPour(2, 1, action))	//3倒2
		{
			Action tem = new Action();	//臨時存儲,一會兒還要變回去
			copy(tem,action);
			//倒水
			pour(2, 1, action);
			fun(action);
			action=tem;
		}
		
		
	}
	//----------------------------------------------------------------
	
	//判斷兩數組值是否相等
	public static boolean isequal(int[] a1 ,int[] a2){
		if(a1.length!=a2.length)
			return false;
		for (int i = 0; i < a2.length; i++) {
			if(a1[i]!=a2[i])
				return false;
		}
		return true;
	}
	
	//判斷是否能倒水,不考慮是否存在以前倒過的情況
	public static boolean canPour(int from, int to,Action action){
		/*
		 * to水桶滿:false
		 * from水桶空:false
		 * from水桶水+to水桶水>to水桶量:false
		 * from=to不能倒
		 */
		if(from==to)
			return false;
		if(action.bucket[from]==0)
			return false;
		if(action.bucket[to]==rule[to])
			return false;
//		if(action.bucket[from]+action.bucket[to]>rule[to])
//			return false;
		return true;
	}
	
	//倒水
	public static void pour(int from,int to,Action action){
		//分兩種,多倒少,少倒多
		int remain = rule[to]-action.bucket[to];
		if(action.bucket[from]>=remain){		//是否多倒少
			action.bucket[from]-=remain;
			action.bucket[to]=rule[to];
		}else{	//少倒多情況
			action.bucket[to]+=action.bucket[from];
			action.bucket[from]=0;
		}
	}
	//把action給tem
	public static void copy(Action tem,Action action){
		for (int i = 0; i < action.bucket.length; i++) {
			tem.bucket[i]=action.bucket[i];
		}
		for (int[] r : action.rout) {
			int[] t = new int[3];
			for (int i = 0; i < r.length; i++) {
				t[i]=r[i];
			}
			tem.rout.add(t);
		}
	}
	
	
}

class Action{
	//定義屬性,並初始化
	int[] bucket = {8,0,0};
	List<int[]> rout = new ArrayList<>();
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "["+bucket[0]+bucket[1]+bucket[2]+"]";
	}
}

運行結果如下,總共16種方法在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章