http://blog.tianya.cn/blogger/post_show.asp?BlogID=956193&PostID=11558356
过河问题:
有三个商人和三个仆人过河,只有一条能装下两个人的船,在河的任何一岸上,如果仆人的人数大于商人的人数,那么该岸上的商人就会有危险。你能不能找出一种安全的渡河方法呢?
过河问题是一个比较出名的问题,借解这个问题的同时,我们来讲讲回溯算法.
首先,我们来分析下问题,商人们要过河,无非就是实现从全部未过河的状态到全部过河的状态.那么,问题可以转变为这样:
左岸:商人3,仆人3=>右岸:商人3,仆人3.即要通过一定的步骤实现状态的转变。而每一个状态(每次都是指船到岸,并不包含船在水中央时的情况)包含有如下几个因素:
1.左岸商人数;
2.左岸仆人数;
3.船在哪岸,即轮到哪岸的人上船.
至于右岸的商人数和仆人数到底是多少,我们不必再记上,因为知道了左岸的人,就可以推算出右岸的人。这样分析,问题便变为:
(3,3,1)=?=>(0,0,0).其中括号描述如下:(左岸商人数,左岸仆人数,船在左岸?1:0).
接下来分析商人有危险的状态。可设一个matrix[4][4]的布尔型数组。matrix[i][j]表示i个商人,j个仆人时是否安全的信息。判断是否安全的标准为哪岸的仆人数大于商人数,这岸的商人就会有危险(当然,哪一岸商人为0时,也是没危险的).用两重循环,可很快解决问题:
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
if(i==j||i==0||i==3);
else matrix[i][j]=true;/*其中为真时,表示有危险.*/
}
再来分析人可以乘船的方案:以(商人数,仆人数)来表示,很明显,乘船的方案为如下5种:{0,2},{1,1},{2,0},{0,1},{1,0}.
接着就是想算法了.一开始状态为(3,3,1),通过5种乘船方案,可转变为5种状态:(3,1,0),(2,2,0),(1,3,0),(3,2,0), (2,3,0).其中matrix[i][j]为true或者不合理或者重复状态的去掉,继续向下拓展.直到出现(0,0,0)为止.从表面上看,这样似乎并不好写。这就要用到了回溯算法.所谓回溯,就是寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解。
回溯方法的步骤如下:
1) 定义一个解空间,它包含问题的解。
2) 用适于搜索的方式组织该空间。
3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。
回溯的这个深层搜索特性,便决定了它通常要用递归来做。应用到这题,显然递归的出口就是出现(0,0,0,)状态的时候。应用回溯的时候,通常有一个要特别要注意的地方,就是深入搜索完成的时候,要记得恢复前一步的状态,以便下一状态的转变。这在下面的程序会用黑体显现出来,这一点一定要注意,笔者自己也曾多次犯此错误。
现把这个问题的全部代码附上:
#i nclude
#i nclude
using namespace std;
bool matrix[4][4],turn;
int record[128][2],pos=1;
bool check[128];
/*判断这一状态是否在前面已经出现过*/
bool isRepeat(int x,int y,bool turn)
{
for(int i=0;i<POS;I++)
if(x==record[i][0]&&y==record[i][1]&&turn==check[i])
return true;
return false;
}
void search(int px,int py)
{
static int goStep[5][2]={{0,2},{1,1},{2,0},{0,1},{1,0}};
int i;
if(px==0&&py==0)
{
for(i=0;i<POS;I++)
{
cout<<"("<<RECORD[I][0]<<","<<RECORD[I][1]<<
")<==>("<<3-record[i][0]<<","<<3-record[i][1]<<")"<<ENDL;
}
system("pause");
}
else if(turn==false)
{
for(i=0;i<5;i++)
{
px-=goStep[i][0];
py-=goStep[i][1];
if(px<0||px>3||py<0||py>3||matrix[px][py]||isRepeat(px,py,turn))
{
px+=goStep[i][0];
py+=goStep[i][1];
continue;
}
record[pos][0]=px,record[pos][1]=py;
check[pos]=turn;
pos++;
turn=(turn==true?false:true);
search(px,py);
pos--;
turn=(turn==true?false:true);
px+=goStep[i][0];
py+=goStep[i][1];
}
}
else
{
for(i=0;i<5;i++)
{
px+=goStep[i][0];
py+=goStep[i][1];
if(px<0||px>3||py<0||py>3||matrix[px][py]||isRepeat(px,py,turn))
{
px-=goStep[i][0];
py-=goStep[i][1];
continue;
}
record[pos][0]=px,record[pos][1]=py;
check[pos]=turn;
pos++;
turn=(turn==true?false:true);
search(px,py);
pos--;
turn=(turn==true?false:true);
px-=goStep[i][0];
py-=goStep[i][1];
}
}
}
int main()
{
int px=3,py=3;
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
if(i==j||i==0||i==3);
else matrix[i][j]=true;
}
record[0][0]=record[0][1]=3;
check[0]=true;
search(px,py);
return 0;
}