题目描述:农夫需要把狼、羊、菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊。请考虑一种方法,让农夫能够安全地安排这些东西和他自己过河。
在大学期间我记得碰到这种问题,第一感觉就是用递归,当时狭义的认为递归算法就是:必须有自身调用自身的函数方法!否则就是没有递归思想。直到最近一次阅读nginx源码中的红黑树实现,发现源码中对红黑树节点插入、旋转的代码也是运用递归思想,但是就是在一个while循环中实现的,于时茅塞顿开!原来递归思想的实现不仅仅局限于一种实现方式,换句话说:函数实现中自身调用自身一定是用了递归思想,但递归思想不一定只有函数自身调用自身一种实现方式。
记得大学时候写的递归特别复杂,限于当时对算法的理解误区,现在也想不清当时的实现细节了。下面是我重新实现的一次,具体代码如下:
/************************************************
*** Author : lijd
*** Date : 2022-11-18
*** Func : 狼羊菜过河算法实现
************************************************/
#include <stdio.h>
// 用二维数组表示变量
// 一维下标 t[i], i = 0 : 为河岸一边,i = 1 : 河对岸
// 二维下标 t[i][j], j = 0:菜, j = 1:羊, j = 2:狼, j = 3:农夫
char t[2][4] = {{1, 1, 1, 1}, {0, 0, 0, 0}};
char name[3][10] = {"菜", "羊", "狼"};
void guohe()
{
int i = 0;
// 当有一个没有过河
while(t[1][0] != 1 || t[1][1] != 1 || t[1][2] != 1 || t[1][3] != 1)
{
// 农夫没过河
if(t[0][3] == 1)
{
// 农夫过河
t[0][3] = 0; t[1][3] = 1;
// 带东西过河
for(i = 0; i < 3; i++)
{
if(t[0][i] == 1)
{
// 假设菜跟狼都已过河
if((t[1][0] == 1 && t[1][2] == 1) && i == 1)
{
// 则带羊过河
t[0][1] = 0; t[1][1] = 1;
printf("农夫带%s过河\n", name[1]);
break;
}
// 假设菜跟狼仅只有一个过河, 不能带羊过河,防止死循环
if((t[1][0] == 1 || t[1][2] == 1) && i == 1)
{
continue;
}
// 假设将i带过河 不满足被吃条件 continue跳过
t[0][i] = 0;
if((t[0][0] == 1 && t[0][1] == 1) ||(t[0][1] == 1 && t[0][2] == 1))
{
t[0][i] = 1;
continue;
}
// 则带i过河
t[0][i] = 0; t[1][i] = 1;
printf("农夫带%s过河\n", name[i]);
break;
}
}
}
// 农夫已过河
else
{
// 农夫回过河
t[0][3] = 1; t[1][3] = 0;
// 判断是否要带回东西过河
if((t[1][0] == 1 && t[1][1] == 1) || (t[1][1] == 1 && t[1][2] == 1))
{
// 让羊回过河
t[0][1] = 1; t[1][1] = 0;
printf("农夫带羊回过河\n");
}else{
printf("农夫自己回过河\n");
}
}
}
}
int main()
{
guohe();
return 0;
}
代码编译运行结果如下: