原本我的队长就推荐我写一些自己的博客作为参考供笔记用,再加上昨天我发老师也鼓励我们要写博客,就借这个契机开始写我自己的博客吧。
本篇我想就老师课后留的一个问题展开,该问题如下:
有一个农夫,一头羊,一头狼,一捆菜在河的一头,问农夫有多少种方式将它们运到河对岸?这个问题还有一点限制,那就是:农夫不在时,狼会吃羊,羊会吃菜,形成了一条食物链。
这个题中的元素有农夫、羊、狼、菜、河流、船。在这个问题里河流自始至终状态没变,可以忽略不计。而且只有农夫可以驾驶船,因此可以将农夫和船看成一个元素。
乍一看问题的关键就在这头羊,只要将羊带走就能破坏这条食物链。由此可推方法如下:
先将羊移到对岸,空手回来,再将狼(此时狼和菜等权重)移到对岸,并且将羊移回来(防止狼吃羊),之后将菜移到对岸,最后将羊移到对岸即可。同理,可以将上述方法中的狼与菜互换,由此可得两种方法。
可问题是, 电脑并不能理解“羊”的重要性,无法得出优先移动羊的结论。这里我们使用深度搜索(dfs)的方法,让电脑找到所有移动的方法,再利用限制条件进行剪枝,帮助电脑找到正确方法。
首先,要表达元素们“过河”与“未过河”两种状态。我决定用bool的true与false来表达这两种状态。F代表农民,V代表蔬菜,S代表羊,W代表狼,"."表示空位。其定义如下:
bool F, W, G, V;
根据老师的问题,初始状态是固定的(所有元素都在河的一边),结束状态则是元素全都移到另一边。操作便是“运输”,我们用“->”表示将要运送的方式。
输入样例
无
输出格式
如果无解,输出”-1“;
若有解,则输出所有可能的解法(由若干行状态组成)
解法间空一行
输出样例
FWGV -> ....
.W.V <- F.G.
FW.V -> ..G.
...V <- FWG.
F.GV -> .W..
..G. <- FW.V
F.G. -> .W.V
.... <- FWGV
FWGV -> ....
.W.V <- F.G.
FW.V -> ..G.
.W.. <- F.GV
FWG. -> ...V
..G. <- FW.V
F.G. -> .W.V
.... <- FWGV
此处解法排列顺序按照——空手——狼——羊——菜的顺序。
解题思路
状态表示确定,算法用深度优先遍历。建立一个二维char数组q,每一步遍历都往q中存放当前状态。由于要避免出现两行相同的状态,还应建立一个bool数组fl来检查当前状态是否存在过。
代码实现
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
const int N = 1010;
bool F, W, V, S;
int flag; // 判断是否找到方法
char q[N][20];//存储每一次运输后的状态
bool fl[20];//判断改状态是否出现过
void pr(int x)
{
if(F) q[x][8] = 'F';
else q[x][0] = 'F';
if(W) q[x][9] = 'W';
else q[x][1] = 'W';
if(V) q[x][10] = 'V';
else q[x][2] = 'V';
if(S) q[x][11] = 'S';
else q[x][3] = 'S';
}
void book(bool F, bool W, bool V, bool S)
{
//标记该状态已经出现过
if(F &&W && V && S) fl[0] = true;
else if(!F && W && V && S) fl[1] = true;
else if(F && !W && V && S) fl[2] = true;
else if(F && W && !V && S) fl[3] = true;
else if(F && W && V && !S) fl[4] = true;
else if(!F && !W && V && S) fl[5] = true;
else if(!F && W && !V && S) fl[6] = true;
else if(!F && W && V && !S) fl[7] = true;
else if(F && !W && !V && S) fl[8] = true;
else if(F && !W && V && !S) fl[9] = true;
else if(F && W && !V && !S) fl[10] = true;
else if(!F && !W && !V && S) fl[11] = true;
else if(!F && !W && V && !S) fl[12] = true;
else if(!F && W && !V && !S) fl[13] = true;
else if(F && !W && !V && !S) fl[14] = true;
else if(!F && !W && !V && !S) fl[15] = true;
}
void dfs(int x)
{
if((W == S && W != F) || (V == S && V != F)) //不可能情况返回
{
dfs(x + 1);
return ;
}
if(F && W && V && S) //全部过河时打印所有可能性
{
flag = 1;
for(int i = 0 ; i < x; i ++ )
{
printf("%s", q[i]);
}
puts("");
return ;
}
for(int i = 1; i <= 4; i ++ )
{
char sps[20] = ".... -> ....";
char sps2[20] = ".... <- ....";
if(!F) strcpy(q[x], sps);
else strcpy(q[x], sps2);
if(!F)
{
F = true; //农夫过河
if(i == 1 && W != F && (!fl[0] || !fl[3] || !fl[4] || !fl[10] ))
{
W = true;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = false;
W = false; //状态复原
}
else if(i == 2 && V != F && (!fl[0] || !fl[2] || !fl[4] || !fl[9] ))
{
V = true;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = false;
V = false;
}
else if(i == 3 && S != F && (!fl[0] || !fl[2] || !fl[3] || !fl[8] ))
{
S = true;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = false;
S = false;
}
else if(i == 4)
{
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = false;
}
}
else
{
F = false; //农夫回岸
if(i == 1 && W != F && (!fl[5] || !fl[11] || !fl[12] || !fl[15] ))
{
W = false;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = true;
W = true;
}
else if(i == 2 && V != F && (!fl[6] || !fl[11] || !fl[13] || !fl[15] ))
{
V = false;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = true;
V = true;
}
else if(i == 3 && S != F && (!fl[7] || !fl[12] || !fl[13] || !fl[15] ))
{
S = false;
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = true;
S = true;
}
else if(i == 4)
{
book(F, W, V, S);
pr(x);
dfs(x + 1);
F = true;
}
}
}
}
int main()
{
char sps[20] = "FWGV -> ....";
strcpy(q[0], sps);
dfs(0);
if(flag != 1) puts("-1"); //未找到方法时输出-1
return 0;
}
这里有一个问题:输出为空,经测试是因为他没有到达最终妇科条件并输出的那条路,希望未来的我能回来把他给解决掉~