商人过河问题

商人过河问题

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名随从划船到对岸。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值