問題描述
有三個容積分別爲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種方法