问题描述
有三个容积分别为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种方法
版权声明:本文为haqiang555原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。