狼羊菜渡河问题

【引言】这两天读司守奎《建模算法与应用》读到一个非常有意思的问题,就是小学时我们都玩过的一道奥赛题,狼羊菜渡河问题。

【问题🐺🐏】

某人带狼、羊和蔬菜渡河,一小船除需要人划外,每次只能载一物过河。而当人不在场时,狼会吃羊,羊会吃菜。问此人应如何渡河?

【最开始思路】小学奥赛题是怎么解的来着...,放弃那种想法,还真的想不到,看看答案,觉得好神奇,居然可以跟我们学过的图论相关联。

【思路】

  • 我们用一个四维向量来表示状态,第一个分量表示人,第二个分量表示狼,第三个分量表示羊,第四个分量表示蔬菜。它们的状态如何展示呢,由于整个问题只有两种情况:在此岸和在对岸,因此我们用0、1两种数字来表示,1表示在此案,0表示在对岸。

由题意,我们知人不在场时,狼会吃羊,所以状态(0,1,1,0)是一个不可行的状态。由此,我们可以列举出所有可行状态:(1 1 1 1) (1 1 1 0) (1 1 0 1) (1 0 1 1) (1 0 1 0) (0 1 0 1) (0 1 0 0) (0 0 1 0) (0 0 0 1) (0 0 0 0)总共10种情况。

  • 对于每一次的渡河行为,都是一次状态的改变。我们现在构造赋权图 G(V,E,W),其顶点集合V=\left \{ v_{1} ,v_{2},...v_{10}\right \}中的顶点分别表示10个可行状态。当且仅当对应的两个可行状态存在可行转移时,两顶点有边相连接,并且边的权重为1,当两个顶点不存在可行转移时,两顶点之间不存在边相连接,我们把其权重设为\infty

因此,什么是可行转移,根据题目可知,可行转移即为一种可以渡河的状态,我们再引入一个四维状态转移向量,用其反映摆渡状态。用1表示渡河,0表示未渡河。则可能出现的状态转移向量有四种:(1,0,0,0) (1,1,0,0) (1,0,1,0) (1,0,0,1)。

  • 现在,问题变成了在图G中寻找一条由状态(1,1,1,1)出发到状态(0,0,0,0)的最短路径。如何得出邻接矩阵成为难点。我们现在有了状态和状态转移向量,因此我们规定状态向量与状态转移向量之间的运算如下:

       1+1=0;1+0=1;0+1=1;0+0=0;(即异或运算)

如果一个可行状态向量加上一个状态转移向量还属于可行状态,那么这两个可行状态对应的顶点之间有边相连接。

【代码】

%%狼羊菜渡河问题
%%清空
clear;clc;
%%建立模型
a = [1 1 1 1;1 1 1 0;1 1 0 1;1 0 1 1;1 0 1 0;0 1 0 1;0 1 0 0;0 0 1 0;0 0 0 1;0 0 0 0];%所有可以出现的状态,即可行状态
b = [1 0 0 0;1 0 0 1;1 1 0 0;1 0 1 0;];%可能出现的转移状态,即状态转移情况
%%邻接矩阵赋值
w = zeros(10);%邻接矩阵初始化
for i = 1:9
    for j = i+1:10
        for k = 1:4
            if findstr(xor(a(i,:),b(k,:)),a(j,:))
                w(i,j) = 1;
            end
        end
    end
end
%%最短路径算法
w = w';%转化为下三角矩阵
c = sparse(w);%构造为稀疏矩阵
[x,y,z] = graphshortestpath(c,1,10,'directed',0);
disp(x);
disp(y);
%%画图
h = view(biograph(c,[],'ShowArrows','off','ShowWeights','off'));
Edges = getedgesbynodeid(h);
set(Edges,'LineColor',[0 0 0]);
set(Edges,'LineWidth',1.5);

【结果】

状态转移顺序: 1     6     3     7     2     8     5    10

第一次运羊过河,空船返回;第二次运菜过河,带羊返回;第三次带狼过河,空船返回;第四次带羊过河。OVER!!!❀❀❀

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
狼羊菜过河问题是一个经典的逻辑问题,也被称为河岸问题问题描述如下:有一只、一只和一棵菜需要过河,但是船只只能容纳两个物品(包括人),且会吃会吃菜。要求找到一种过河方案,使得和菜都能安全到达对岸。 在MATLAB中,可以使用编程来解决这个问题。以下是一个简单的MATLAB代码示: ```matlab function crossingRiver() % 初始化初始状态 state = [1, 1, 1]; % [, , 菜],1表示在起始岸,0表示在对岸 dispState(state); % 开始过河 while ~isequal(state, [0, 0, 0]) % 生成所有可能的下一步状态 nextStates = generateNextStates(state); % 遍历所有可能的下一步状态 for i = 1:size(nextStates, 1) nextState = nextStates(i, :); % 检查下一步状态是否合法 if isValidState(nextState) % 更新当前状态 state = nextState; dispState(state); break; end end end disp("成功过河!"); end function dispState(state) % 根据状态显示当前情况 if state(1) == 1 wolf = ""; else wolf = " "; end if state(2) == 1 goat = ""; else goat = " "; end if state(3) == 1 cabbage = "菜"; else cabbage = " "; end disp("起始岸:" + wolf + " " + goat + " " + cabbage); disp("对岸: " + ~state(1) + " " + ~state(2) + " " + ~state(3)); disp("--"); end function nextStates = generateNextStates(state) % 生成所有可能的下一步状态 nextStates = []; for i = 1:3 for j = 1:3 if i ~= j nextState = state; nextState(i) = ~nextState(i); nextState(j) = ~nextState(j); nextStates = [nextStates; nextState]; end end end end function valid = isValidState(state) % 检查状态是否合法 if (state(2) == state(3)) && (state(1) ~= state(2)) valid = false; elseif (state(1) == state(2)) && (state(2) ~= state(3)) valid = false; else valid = true; end end ``` 这段代码实现了一个简单的狼羊菜过河问题的求解。运行`crossingRiver`函数即可开始求解过河方案。代码中使用了状态表示和状态转移的方法来生成所有可能的下一步状态,并通过判断状态的合法性来选择下一步的动作。最终输出成功过河的提示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值