多对商人过河数学建模.docx
《多对商人过河数学建模.docx》由会员分享,可在线阅读,更多相关《多对商人过河数学建模.docx(16页珍藏版)》请在冰豆网上搜索。
多对商人过河数学建模
多对商仆过河问题
12对商人过河
——(算法中多少对可以改变,此为N=12的时候,稍加修改便可以成为你需要的对数解决方案)
摘要
本文针对商人安全渡河的问题,采用多步决策的过程建立数学模型,求解得到了在随从没有杀人越货的情况下的渡河方案。
对于本题而言,在12名商人、12名随从、船的最大容量为2的情况下,首先定义了渡河前此岸的状态,并设安全渡河条件下的状态集定义为允许状态集合,接着得到渡河方案的允许决策集合,然后得到状态随渡河方案变化的规律,最后利用dijkstra算法,并利用MicrosoftVisualC++6.0软件,编译运行程序得到了一种商人安全渡河的方案。
但是,本文不仅仅是为了拼凑出一个可行方案,而是希望能找到求解这类问题的规律性,并建立数学模型,用以解决更为广泛的问题。
基于此目的,利用了dijkstra算法,得到最短路径的最优解。
但同时由于该算法遍历计算的节点很多,所以效率低,而且当有多个最短距离时,不能够将所有符合条件的情况逐一列出。
我们通过对程序的改善,使可以运行比较多的将符合条件的情况列出来。
1问题重述
十二名商人各带一个随从乘船渡河,一只小船只能容纳二人,由他们自己划行。
在河的任意一岸,一旦随从的人数比商人多,商人就有危险.但是如何乘船渡河的大权掌握在商人们手中。
商人们怎样才能安全渡河呢?
同时,推广到M名商人带M名随从又如何?
2问题分析
安全渡河问题可以看成一个多步决策过程。
每一步,即船由此岸驶向彼岸或从彼岸驶回此岸,都要对船上的人员(商人随从各几人)作出决策,在保证安全的前提下(两岸的商人数都不比随从数少),在有限步内使人员全部过河。
用状态(变量)表示某一岸的人员状况,决策(变量)表示船上的人员状况,可以找出状态随决策变化的规律。
问题转化为在状态的允许变化范围内(即安全渡河条件),确定每一步的决策,达到渡河的目的。
此类智力问题经过思考,可以拼凑出一个可行方案。
但是,我们现在希望能找到求解这类问题的规律性,并建立数学模型,用以解决更为广泛的问题。
3模型假设及符号说明
3.1模型假设
(1)每个商人和随从都会划船;
(2)只有一条船,且每条船上最多只能乘坐两个人;
(3)所有商人与随从之间没有矛盾,不会出现两人不愿意坐一条船的现象;
(4)船在渡河的过程中不受外界环境的影响。
3.2符号说明
初始状态下,商人和随从所在的一岸;
初始状态下,商人和随从欲到达的一岸;
S商仆对数
K船最多载人的数目
4模型的建立与求解
4.1模型的建立
根据题意,可以作出商人渡河初始状态的示意图:
渡河目的:
——>
(选择
岸为参考点)
4.2C++程序解决
根据
(1)
(2)(3)式,通过利用matlab编写一段程序来求解多步决策问题是可行的,但是当商人和随从数都不多的情况下还可以用平面坐标法解此模型更为方便。
接下来,我们先用平面坐标法求解此模型,最后再使用计算机仿真,对求解的结果进行验证,并给予推广。
4.2.1C++程序代码
//约束条件:
岸上仆人不能多于商人数
#include
usingnamespacestd;
structNode
{intnMer;
intnSer;
intlength;
};
classA
{
public:
A();
~A();
voidTspt();//过河的动作
voiddoLeft(intnhead,intntail,intnlength);
private:
boolislegal(intnm,intns);//判断是否满足约束条件,满足为true
Node*funTspt(intnm,intns,boolflag);//添加STEP[head]可以向后延伸的节点
boolnoRepeat(intnm,intns);//没有重复返回TRUE
voidfunshow(inta[][2],intntail);
boolfunLeft(Nodend,intb1,intb2,intn);
voidshow(ints[],intp[][2],int&top,int&count,inta[]);
inthead;
inttail;
intn;//商仆的对数
intnB;//船最多的载人数目
Node*STEP;
};
A:
:
~A()
{
free(STEP);
}
A:
:
A()
{
cout<<"请输入商仆的对数S=";
F:
cin>>n;
if(n==1)
{
nB=2;
cout<<"船最多载人的数目K="<}
elseif(n==2)
{
cout<<"船最多载人的数目可以取:
";
for(intx=n;x<=2*n;x++)
{
cout<}
cout<cout<<"请输入船最多载人的数目K=";
cin>>nB;
}
elseif(n==3)
{
cout<<"船最多载人的数目可以取:
";
for(intx=n-1;x<=2*n;x++)
{
cout<}
cout<cout<<"请输入船最多载人的数目K=";
cin>>nB;
}
elseif(n==4)
{
cout<<"船最多载人的数目可以取:
";
for(intx=n-1;x<=2*n;x++)
{
cout<}
cout<cout<<"请输入船最多载人的数目K=";
cin>>nB;
}
elseif(n>=5&&n<=100)
{
cout<<"船最多载人的数目可以取:
";
for(intx=4;x<=2*n;x++)
{
cout<}
cout<cout<<"请输入船最多载人的数目K=";
cin>>nB;
}
elseif(n<1||n>100)
{
cout<<"本程序仅在S=(0…100)以内保证其正确性"<cout<<"请重新输入商仆的对数S=";
gotoF;
}
STEP=(Node*)malloc(sizeof(Node)*10000);
memset(STEP,0,sizeof(Node)*10000);
head=tail=0;
STEP[0].nMer=STEP[0].nSer=n;
}
intmain()
{
cout<<"问题描述:
S个商人各带一个随从乘船过河,一只小船只能容纳K人,由他们自己划船。
商人们窃听到随从们密谋,在河的任意一岸上,只要随从的人数比商人多,就杀掉商人。
但是如何乘船渡河的决策权在商人手中,商人们如何安排渡河计划确保自身安全?
"<Aa;
a.Tspt();
return0;
}
voidA:
:
show(ints[],intp[][2],int&top,int&count,inta[])
{
if(top==-1)
return;
//已找到目标状态需,输出数据
if(top==STEP[head].length)
{
cout<<"***********"<<++count<<"***********"<funshow(p,top+1);
B:
top--;
if(top==-1)
return;
C:
s[top]--;
if(STEP[(s[top])].length!
=top)//退过了
{
s[top]=a[top];
gotoB;
}
if(funLeft(STEP[(s[top])],p[top-1][0],p[top-1][1],top-1)==false)
gotoC;
p[top][0]=STEP[(s[top])].nMer;
p[top][1]=STEP[(s[top])].nSer;
show(s,p,top,count,a);
return;
}
//在中间加入节点STEP[(s[top+1])]
if(funLeft(STEP[(s[top+1])],p[top][0],p[top][1],top)==true)//符合条件
{
top++;
p[top][0]=STEP[(s[top])].nMer;
p[top][1]=STEP[(s[top])].nSer;
show(s,p,top,count,a);
return;
}
else//不符合条件
{
E:
s[top+1]--;
if(STEP[(s[top+1])].length==top)//退过了,到了下一层
{
s[top+1]=a[top+1];
D:
s[top]--;
if(STEP[(s[top])].length!
=top)//退过了,到了下一层
{
for(inti=top;i<=STEP[head].length;i++)
s[i]=a[i];
top--;
if(top==-1)
return;
gotoD;
}
if(top==0)
return;
if(funLeft(STEP[(s[top])],p[top-1][0],p[top-1][1],top-1)==false)
gotoD;
p[top][0]=STEP[(s[top])].nMer;
p[top][1]=STEP[(s[top])].nSer;
show(s,p,top,count,a);
return;
}
if(funLeft(STEP[(s[top+1])],p[top][0],p[top][1],top)==false)
gotoE;
top++;
p[top][0]=STEP[(s[top])].nMer;
p[top][1]=STEP[(s[top])].nSer;
show(s,p,top,count,a);
}
}
voidA:
:
doLeft(intnhead,intntail,intnlength)
{
inta[1000];
inta1[1000];
intsp[1000][2];
boolflag=false;
memset(a,0xff,4000);
memset(a1,0xff,4000);
memset(sp,0xff,8000);
if(STEP[head].length%2==0)
flag=true;
while(STEP[head].length==nlength-1)
{
funTspt(STEP[head].nMer,STEP[head].nSer,flag);
head++;
}
for(inti=0;i
{
a[(STEP[i].length)]=i;
a1[(STEP[i].length)]=i;
}
sp[0][0]=sp[0][1]=n;
STEP[head].nMer=STEP[head].nSer=0;
inttop=0;
intcount=0;
show(a1,sp,top,count,a);
}
boolA:
:
funLeft(Nodend,intb1,intb2,intn)
{
boolflag=abs(nd.nMer-b1)+abs(nd.nSer-b2)&&abs(nd.nMer-b1)+abs(nd.nSer-b2)>0;
if(flag==false)
returnfalse;
if(n%2==0&&b1>=nd.nMer&&b2>=nd.nSer)
returntrue;
if(n%2==1&&b1<=nd.nMer&&b2<=nd.nSer)
returntrue;
returnfalse;
}
voidA:
:
Tspt()
{
Node*temp=newNode;
temp=NULL;
boolflag=false;
while(head<=tail)
{
if(STEP[head].length%2==0)
flag=true;
else
flag=false;
temp=funTspt(STEP[head].nMer,STEP[head].nSer,flag);
if(NULL!
=temp)
break;
head++;
}
if(head>tail)
{
cout<<"此问题无解!
"<exit
(1);
}
doLeft(temp->nMer,temp->nSer,temp->length);//temp->nMer表示head
deletetemp;
}
Node*A:
:
funTspt(intnm,intns,boolflag)
{//flag==true向对岸运输
Node*nd=NULL;
inttemp=1;
inttM=STEP[head].nMer;//可供运输的商人数
inttS=STEP[head].nSer;//可供运输的仆人数
if(flag==false)//向此岸运输
{
tM=n-STEP[head].nMer;
tS=n-STEP[head].nSer;
temp=-1;
}
for(inti=0;i{
for(intj=0;j{
if(i+j==0)
continue;
intp=STEP[head].nMer-temp*i;
intq=STEP[head].nSer-temp*j;
if(islegal(p,q)==true&&noRepeat(p,q)==true)
{
if(p==0&&q==0)
{
tail++;
STEP[tail].length=STEP[head].length+1;
STEP[tail].nMer=p;
STEP[tail].nSer=q;
nd=(Node*)malloc(sizeof(Node));
nd->length=STEP[head].length+1;
nd->nMer=head;
nd->nSer=tail;
returnnd;
}
tail++;
STEP[tail].length=STEP[head].length+1;
STEP[tail].nMer=p;
STEP[tail].nSer=q;
}
}
}
returnnd;
}
boolA:
:
noRepeat(intnm,intns)
{
intj1=0;
if(STEP[head].length%2==0)
j1=1;
for(inti=j1;i{
if(STEP[i].length%2==j1&&nm==STEP[i].nMer&&ns==STEP[i].nSer)
returnfalse;
}
returntrue;
}
boolA:
:
islegal(intnm,intns)
{//商人数少于仆人数或者商人数为0
if((nm==0)||(nm==n)||(nm==ns))
returntrue;
returnfalse;
}
voidA:
:
funshow(inta[][2],intntail)
{
cout<cout<<"商人数仆人数"<for(inti=0;i{
cout<<"第"<
if(i!
=ntail-1&&i%2==0)
cout<<"-->("<<elseif(i!
=ntail-1&&i%2==1)
cout<<"<--("<<}
cout<}
4.2.2运行结果显示
图1:
从图中看出当商仆对数为12时候,船的容量为2时,此问题无解,即这种情况无法安全渡河。
图2:
从图中可以看出,当商仆为12对,船的容量为4人时,有解,即可以安全过河。
扩展情况:
图3:
从图中可以看出,商仆对数为3,容量为2,3,4,5,6的时候,均可以安全过河。
当容量为2时并且有4种方式。
通过计算机运行此c++程序,当题目中给定出任意数量的商人,随从,以及规定出任意船的容量,都可以判断出“商人们能否安全渡河?
”以及解决“如果能,那么安全渡河的方案是什么?
”的问题。
从而使这个模型更具有一定的推广价值。
5模型的评价与改进
5.1模型的评价
5.1.1模型的优点
(1)采用了较为成熟的数学理论建立模型,可行度比较高;
(2)运用程序显示多个路径,比较直观;
(3)模型的求解运用了强大的MicrosoftVisualC++6.0软件,结果可信度高,便于推广;
(4)通过C++程序,能判断出“当任意个商人﹑任意个随从﹑船的容量任意时,商人能否安全渡河?
”及解决了“如果能,那么渡河方案又是什么?
”的问题,使得所建模型更加全面。
5.1.2模型的缺点
(1)没有找到商人数﹑随从数及船的容量之间的数量关系;
(2)没有考虑到实际生活中,在安全渡河的前提下,商人过河的优先级应高于随从。
5.2模型的改进
基于以上求解模型用到的方法,我们明显意识到了结果考虑到的不够全面。
为此,我们编写的C++算法程序,通过相应程序求解前面模型得到最短路径的最优解。
但同时当有多个解决方案时,能够将所有符合条件的情况逐一列出。
综合以上的努力,我们与致力于研究出一套方案:
即给出任意个商人与任意个随从以及船的容量任意的时候,都可以给出安全渡河的方案;并且在给出商人数、随从数、船的容量中任意两者,并要使其能够安全渡河情况下的第三者的取值范围,以及得到最优的渡河方案。
由于水平有限,我们只能提出这个美好的想法,用某种方法能把在所有安全状态集合和决策集合中,搜索出所有可能的解,从而从其中找出最优的解。
6模型的推广
“商人渡河”模型适合于解决很多问题,如“传教士与野蛮人渡河”,“印度夫妻渡河”等。
这些问题本质上都是相同或相似的,由此可见这个趣味问题流传的广泛性。
另外还有所谓“人狗鸡米过河”问题,也是颇有趣味的,人、狗、鸡、米均要过河,船需人划,而船上至多还可载一物,但若人不在时,狗会吃鸡,鸡会吃米,问如何设计安全过河方案。
我们完全可以仿照商人渡河问题建立一个多步决策模型,将上述算法稍作修改,就可以得到它的解。
这里就不再赘述了。
另外,用一定容积的若干油瓶倒出一定量的油的问题也属此类问题。