商人过河问题(n个)-Matlab

n个商人过河问题

Description

        商人过河问题:n名商人各带一名随从过河,一只小船只能容纳z个人,随从们约定,只要在河的任何一岸,一旦随从人数多于商人人数就杀人越货,但是商人们知道了他们的约定,并且如何过河的大权掌握在商人们手中,商人们该采取怎样的策略才能安全过河并且渡河次数最少呢?

Explaination

  1. 先从一般的例子开始讨论,通过3名商人各带一名随从过河,一只小船只能容纳3个人,从此岸的角度确定安全矩阵。再递推,确定n名商人各带一名随从过河,一只小船只能容纳z个人的安全状态矩阵(0、1)的规律
  2. 然后分类讨论,设立判定条件,来讨论什么情况下不存在安全过河状态以及对安全矩阵的遍历,通过设定单元数组来储存渡河所有过程,以便于后续查找和判断
  3. 最后通过判断此岸商人、仆人是否都已经到达彼岸来选择出最少渡河次数,并输出一组结果;如果不存在安全到达的情况则输出No answer!

Example

  • 当n=3,z=3时,渡河的最少次数、此岸的商人数和仆人数变化如下:
  • k =
    
         5
    
    W =
    
         3     3
         3     1
         3     2
         0     2
         0     3
         0     0

  • 当n=5,z=3时,渡河的最少次数、此岸的商人数和仆人数变化如下:
    k =
    
             11
    
    W =
    
              5     5
              5     3
              5     4
              5     1
              5     2
              2     2
              3     3
              0     3
              0     4
              0     2
              0     3
              0     0

Code

:对于不同情况的m、z只需要修改代码中的m、z即可。

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


<第一次写博客>ヽ(○^㉨^)ノ♪ 

猜你喜欢

转载自blog.csdn.net/jly164260234/article/details/80659249
今日推荐