a)8/5/3 将3给甲;剩下为8/5/0
b)8/2/3 将2给乙;剩下为8/0/3
c)8/3/0
d)5/3/3
e)5/6/0
f)2/6/3
g)2/8/1 将1给甲;剩下为2/8/0
h)2/5/3
i)0/7/3
j)3/7/0
k)3/4/3
l)6/4/0
m)6/1/3 将1给丙;剩下为6/0/3
n)8/0/1 将1给丁;剩下为8/0/0
o)5/0/3
p)5/3/0
q)2/3/3 将2给乙;3给丙;丁即可
现在我们先缝隙一下这个问题该如何实现,首先,我们将他抽象成7个三种类型的瓶子,这样就可以很直观的叠代求解。对于每一时刻的每一状态,都可能出现好几种的倒酒方法,我们从其中选出一种,然后继续构造倒酒方法,直到找到可以倒完酒的方法,如果再找不出可行的到酒方法的情况下,将问题回溯,进行下一个方法的继续求解!下边是倒酒算法的核心函数:
//倒酒的核心函数,产生一个正确的倒酒序列
int GenerateResult(int Box[7],list<MOVE>& list_r)
{
if(!IsEnd(Box))
{
list<MOVE> list_temp;
int i;
i=GenAllCurrentMove(Box,list_temp);//产生当前局面的所有走法
if(i==0)//如果没有走法返回上一层;
{
return 0;
}
//找出当前局面的所有走法是否能产生正确的倒酒序列
list<MOVE>::iterator iter=list_temp.begin();
for(iter;iter!=list_temp.end();iter++)
{
list_r.push_back(*iter);
MakeMove(Box,*iter);
int res;
res=GenerateResult(Box,list_r);
//当前情况下产生以后的所有倒酒方法都不成功,则继续下一个寻求
if(res==0)
{
UnMakeMove(Box,*iter);
//删除刚才插入的走发
list_r.pop_back();
}
//如果在当前情况下找到解
else
return 1;
}
//如果当前层没有解,返回上层
return 0;
}
//如果结束
else
return 1;
}
对于没一个局面的所有走法的求解,可以列举所有的可行的走法:
//产生当前局面所有的倒酒方法,保存在list_temp中
int GenAllCurrentMove(int Box[7],list<MOVE>& list_temp)
{
int count=0;//倒酒方法的数量
int max[7]={8,8,3,4,4,4,4};//每个酒瓶或者人的最大容量
for(int k=0;k<3;k++)
{
//当前酒瓶空,继续下一个酒瓶
if(Box[k]==0)
continue;
//循环从左到后找出可以倒酒的方法
for(int det=0;det<7;det++)
{
//如果目标瓶和原瓶相同,继续下一个
if(det==k)
continue;
//瓶之间互相到
if(det<3)
{
//由于0瓶和1瓶是同性质的,所以当一个瓶为空时,不能将另一个瓶里的酒往里面到
//目标不满
if(((k==2||det==2)?1:Box[det]>0)&&Box[det]<max[det])
{
MOVE pmove;
//如果目标和原瓶的酒的数量相加大于目标最大的数量
if(Box[k]+Box[det]>max[det])
{
pmove.Value=max[det]-Box[det];
}
//否则
else
pmove.Value=Box[k];
pmove.From=k;
pmove.To=det;
if(IsValid(pmove))
{
list_temp.push_back(pmove);
count++;
}
}
}
//如果直接给人喝的话
if(det>=3)
{
//如果当前这个人还未分够4斤酒
if(Box[det]+Box[k]<=4)
{
MOVE pmove;
pmove.From=k;
pmove.To=det;
pmove.Value=Box[k];
list_temp.push_back(pmove);
count++;
//从左到右的进行倒酒,碰到当前还一点都未分到酒的人时,此次倒酒结束
if(Box[det]==0)
break;
}//if
}//if
}
}
return count;
}
当然,倒酒的时候,得判断当前到法是否可行,如果来回倒酒或则是倒的时候出现以前倒过的局面,则应该去掉当前的倒酒方法:
//判断当前的走发是否将产生死循环或者将产生一个重复的倒后局面
int IsValid(MOVE& move)
{
MOVE lastmove;//上一次的倒酒方法
int temp[7]={8,8,0,0,0,0,0};
//按倒酒方法倒酒
MakeMove(Boxes,move);
list<MOVE>::iterator iter=list_rslt.begin();
//判断此次倒酒是否将产生与以前重复的局面,从而将次方法剪枝掉
//如以前出现5 8 3,倒酒后产生5 8 3或者8 5 3
for(iter;iter!=list_rslt.end();iter++)
{
MakeMove(temp,*iter);
//如果产生以前已经产生的局面
if((temp[0]==Boxes[0]&&temp[1]==Boxes[1]&&temp[2]==Boxes[2])||
(temp[1]==Boxes[0]&&temp[0]==Boxes[1]&&temp[2]==Boxes[2]))
{
UnMakeMove(Boxes,move);
return 0;
}
lastmove=*iter;
}
//恢复倒酒
UnMakeMove(Boxes,move);
//如果来回倒酒
if(lastmove.From==move.To&&lastmove.To==move.From&&lastmove.Value==move.Value)
return 0;
return 1;
}
//判断是否已经结束
int IsEnd(int Box[7])
{
//如果三个酒瓶都空了,则表示倒酒结束
if(Box[0]==0&&Box[1]==0&&Box[2]==0)
return 1;
else
return 0;
}
//按当前的倒酒方法倒酒
void MakeMove(int Box[7],MOVE& move)
{
Box[move.From]-=move.Value;
Box[move.To]+=move.Value;
}
//取消上次的倒酒
void UnMakeMove(int Box[7],MOVE& move)
{
Box[move.From]+=move.Value;
Box[move.To]-=move.Value;
}
//打印输出
void Print()
{
int temp[7]={8,8,0,0,0,0,0};
list<MOVE>::iterator iter=list_rslt.begin();
cout<<"/*有两个容量为8斤的酒瓶装满酒,另有一个三斤的空酒瓶,"<<"\n"
<<"利用这三个没有标度的酒瓶将这些酒分给四个人喝,每个人分4斤酒。*/\n";
cout<<" 求解结果\n";
cout<<"8 8 0 0 0 0 0"<<endl;
//控制输出条件
//count1为前一次的所有酒瓶里的酒的和,count2为后一次的
int count1,count2;
count1=16;
count2=0;
for(iter;iter!=list_rslt.end();iter++)
{
MakeMove(temp,*iter);
count2=temp[0]+temp[1]+temp[2];
if(count1!=count2)
{
cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2]<<" "<<temp[3]<<" "<<temp[4]<<" "
<<temp[5]<<" "<<temp[6]<<endl;
}
else
cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2]<<endl;
count1=count2;
}
}
下边是主函数体:
#include<list>
struct MOVE{
int From;//从那儿倒
int To;//倒去那儿
int Value;//倒的斤数
};
//产生的走法链表 ,全局变量
list<MOVE> list_rslt;
//放酒的酒瓶Box[0..1]代表装8斤酒的酒瓶,Box[2]代表可以装三斤酒的酒瓶
//Box[3..7]代表喝酒的四个人
int Boxes[7]={8,8,0,0,0,0,0};
//函数声明
int IsEnd(int Box[7]);
int IsValid(MOVE& move);
int GenAllCurrentMove(int Box[7],list<MOVE>& list_temp);
void MakeMove(int Box[7],MOVE& move);
void UnMakeMove(int Box[7],MOVE& move);
int GenerateResult(int Box[7],list<MOVE>& list_r);
void Print();
void main()
{
if(GenerateResult(Boxes,list_rslt))
{
Print();
}
else
cout<<"NO Result!";
}