商人过河问题
1.问题描述
n名商人带n个随从乘船渡河,一只小船能容纳k人,由他们自己划行。随从们秘密商定,在河的任一岸,一旦随从的人数比商人多,就杀人越货。当然如何渡船的方案则由商人们决定,那么怎样才能确保商人安全渡河呢
2.模型假设
2.1 假设过河过程中不会发生意外
2.2 假设当随从数量多于商人数量时,不改变杀人越货的计划
2.3 假设所有人最终都到达河对岸
3.符号说明
M
\mathcal{M}
M:表示商人的数量
N
\mathcal{N}
N:表示随从的数量
Z:表示河的对岸和此岸
K:表示小船的容量
k:表示小船的乘坐的人数
M:表示此岸的商人数量
m:表示对岸的商人数量
N:表示此岸的随从数量
n:表示对岸的随从数量
4.模型分析
本题针对商人们能否安全过河,选择一种合理的过河方案,是一个多步决策问题。通过对每一次过河方案的筛选优化,最终得到商人们全部安全过河的最优决策方案。对每一次的过河过程看成一个随机决策状态量,商人们能够安全到达彼岸或此岸。我们可以看成目标决策允许的状态量,通过对允许的状态量的层层筛选,从而得到过河的目标。
5.模型的建立和求解
5.1模型的建立
本体为多步决策模型,每一次过河都是状态量的转移过程,可以用三维向量表示(M,N,Z)表示
M的取值范围:
{
0
,
1
,
2
,
⋯
,
n
}
\left \{ 0,1,2,\cdots,n \right \}
{0,1,2,⋯,n}
N的取值范围:
{
0
,
1
,
2
,
⋯
,
n
}
\left \{ 0,1,2,\cdots,n \right \}
{0,1,2,⋯,n}
Z
=
{
1
,
表示划船到河的彼岸
0
,
表示划船到河的此岸
Z=\left\{\begin{array}{l} 1, \text { 表示划船到河的彼岸 } \\ 0, \text { 表示划船到河的此岸 } \end{array}\right.
Z={1, 表示划船到河的彼岸 0, 表示划船到河的此岸
那么允许状态量(即两岸同时必须满足(M≥N)且(m≥n)
5.2模型的求解
模型要求从(n,n,1)开始,经过对每次过河的安全状态量的选择,最终安全到达(0,0,0)
根据题意,状态转移必须满足以下规则:
(1)Z从1变0或从0变1交替进行。
(2)z从1变为0,即从河的此岸到达彼岸,此岸的人数减少
1
,
2
,
⋯
,
K
1,2, \cdots ,K
1,2,⋯,K,即从(M,N,1)→(m,n,0)时,两岸人数满足M≥N且m≥n
代码实现如下
% Matlab
clear;
home;
m=3; %商人、仆人数
z=3; %船上可载人数
m=m+1; %矩阵中不存在0位置
A=zeros(m); %列表示商人,行表示仆人
for i=0:m-1 %寻找安全状态,1为安全,0为不安全
for j=0:m-1
if (i==j)||(i==m-1)||(i==0)
A(i+1,j+1)=1;
end
end
end
s={}; %用来存放路径
p=1; %用来表示放在每一行的第几列
R=[]; %特殊情况
s{1,1}=[m,m,1]; %[a,b,c]a表示此岸商人数,b表示此岸仆人数,c表示由上面情况c推的
for t=1:100 %循环上界适当
flag=0; %用来判断是否重复
trump=0; %用来判断是否已经存在可安全渡河的情况
k=t; %k表示渡河次数
if z>=2*(m-1) %当船载人数大于或等于总人数
k=1
R=[m-1,m-1;0,0]
break;
end
for a=1:sum(~cellfun(@isempty, s(k,:))) %表示有几种情况(相对于同一个k来说)
if a==1 %第一种情况,从第一个列开始存
p=1;
end
n1=s{k,a}(1,1); %赋值,商人数
n2=s{k,a}(1,2); %赋值,仆人数
for i=0:z %渡河
for j=0:z
if (i+j>=1 && i+j<=z) && (n1+i*(-1)^k<=m && n1+i*(-1)^k>=1) && (n2+j*(-1)^k<=m && n2+j*(-1)^k>=1) && (A(n1+i*(-1)^k,n2+j*(-1)^k)==1)
%条件判断
if k>=2 %判断是否重复
for e=1:sum(~cellfun(@isempty, s(k-1,:)))
if (s{k-1,e}(1,1)==n1+i*(-1)^k) && (s{k-1,e}(1,2)==n2+j*(-1)^k)
flag=1; %如果发现重复,则flag==1
break;
end
end
end
if flag==0 %不重复存入
s{k+1,p}=[n1+i*(-1)^k,n2+j*(-1)^k,a];
p=p+1;
end
flag=0; %每一次判断之后flag都要归0
end
end
end
end
k=k+1; %一次考虑完后k+1
s{k+1,1}=[]; %往下创建空cell
if sum(~cellfun(@isempty, s(k,:)))==0 %判断是否有新的情况生成,0表示s{k,:}全部为空
'No answer!'
break;
end
for win=1:sum(~cellfun(@isempty, s(k,:))) %判断是否已有完成任务的情况
if s{k,win}(1,1)==1 && s{k,win}(1,2)==1
trump=1;
break;
end
end
if trump==1
k=k-1
break;
end
end
if trump==1 %输出一组结果
W=[];
qq=win;
for oo=1:k+1
W(oo,1)=s{k+2-oo,qq}(1,1)-1;
W(oo,2)=s{k+2-oo,qq}(1,2)-1;
qq=s{k+2-oo,qq}(1,3);
end
W=flipud(W)
end
输出结果如下
1 k =
2
3 5
4
5 W=
6
7 3 3
8 3 1
9 3 2
10 0 2
11 0 3
12 0 0
当3名商人3名随从,且船最多坐3人时,最少需要5次,第一次运送2名随从,第二次一名随从划船回来,第三次运送3名商人,第四次1名随从划船回来,第五次3名随从划船到对岸。