人鬼渡河
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
R
第8步:1鬼上船东渡,东岸有3鬼
8 G 7 6 2 3 0,3 1,3 2,3
2 1,2 2,2 2,1
3,3 3,2 3,1
1
1 0,1 1,1
3
0,0
0 1 5 2
3,0 4 3
R
第9步:2鬼上船西渡,东岸有1鬼
8 G 7 6 9 3 0,3 1,3
2 1,2
2 2,3
2,2 2,1
3,3 3,2 3,1
3个人都在东岸的4个点
G 3 0,3
2 1 0,2 0,1
1,3 1,2 1,1
2,3 2,2 2,1
3,3 3,2 3,1
0,0
0
1,0
1
2,0
2
3,0
3
3 个 人 都 在 东 岸 R
3个人都在西岸的4个点 3 个 人 2 都 在 1 西 岸
G 3 0,3
0,2 0,1
1,3 1,2 1,1
2,3 2,2 2,1
人鬼渡河
人鬼渡河的规则
目标是将东岸的3人3鬼通过一只小船转移 到西岸,希望以尽可能少的摆渡次数。 船的容量有限,一次最多只能坐2人(或2 鬼或1人1鬼)。 无论是在河的东岸还是在河的西岸,一旦 鬼数多于人数,则人被鬼扔到河中。 怎样渡河的大权掌握在人的手中。 只求一种渡河方案。
人鬼渡河的安全方案
0,0
0
1,0 1
2,0 2 4
3,0 3
R
第5步:2人上船西渡,东岸有1人1鬼
G 3 0,3
2 0,2 1 0,1
2
1,3 1,2 1,1 2,3 2,2 2,1 3,3 3,2 3,1 3 1
0,0
0 1 5 2
3,0 4 3
R
第6步:1人1鬼上船东渡,东岸有2人2鬼
G 3 0,3
2 0,2
if(mu>3||mv>3||mu<0||mv<0||(mu==3 && mv==3 )) continue; //取消越界和重复 AQ=((mu==3)||(mu==0)||(mu==1 && mv==1)||(mu==2 && mv==2)); //状态安全点 CHF=(flag1)&&(flag2); //重复(达到过1人1鬼和2人2鬼的状态) if(mu==0 ||AQ&&(!CHF) ) //如果3人已达西岸,或安全且不重复 {//if //条件成立 u=mu; //存东岸人数
记R----人;G----鬼。 K---船行次数,从东岸到西岸或从西岸到东 岸记1次。 显然, 船从东到西,K为奇数; 船从西到东,K为偶数。
渡河前东岸状态定义
用2维向量SK=(RK,GK)定义为第k次渡河 前东岸的渡河状态。 允许渡河东岸状态集合 S={ (R,G)| R=0, G=0,1,2,3; R=3, G=0,1,2,3; R=1, G=1; R=2, G=2; }
1,0
1
2,0
2
3,0
3
3 个 人 都 在 东 岸 R
0
避免重复问题 让 ( 1,1 )与( 2,2 ) 各出现一次, 编程时要考虑这一点。
东岸状态图中的安全点 与不安全点
G 3 0,3
2 0,2 1 0,1
1,3 1,2 1,1
2,3 2,2 2,1
3,3 3,2 3,1
0,0
0
1,0 1
2,0 2
display(k+1);
return 0; }
//显示渡河过程
//主函数结束
void display(int p) {
//显示渡河过程子函数
for(int i=1;i<=p;i++) //循环 cout<<i<<":("<<s[i].r<<","<<s[i].g<<")"<<endl; }
程序运行结果
1
1 0,1 1,1
3
0,0
0 1 5 2
3,0 4 3
R
第10步:1鬼上船东渡,东岸有2鬼
8 G 7 6 2 9 3 0,3 1,3 2,3
2 1,2 2,2 2,1
3,3 3,2 3,1
1
1 0,1 1,1 10
3
0,0
0 1 5 2 4
3,0 3
R
第11步:2鬼上船西渡,东岸空
起始状态 3,3 1
状态:用结构数组来描述 struct state //定义名为state的结构 { int r; //人数 int g; //鬼数 }; state s[15]; //定义结构数组 s[1].r=3; //初始状态东岸有3人 s[1].g=3; //初始状态东岸有3鬼 s[k].r=u; //第k个状态东岸有u人 s[k].g=v; //第k个状态东岸有v鬼 s[n+1].r=0; //第n+1个状态东岸有0人 s[n+1].g=0; //第n+1个状态东岸有0鬼
1:(3,3)
2:(3,1) 3:(3,2) 4:(3,0) 5:(3,1) 6:(1,1) 7:(2,2) 8:(0,2) 9:(0,3) 10:(0,1) 11:(0,2) 12:(0,0)
状态转移过程图
1:(3,3) 9 0,3 2,2 7
初始状态 1 3,3
2:(3,1) 3:(3,2) 4:(3,0) 11 8 0,2 5:(3,1) 6:(1,1) 7:(2,2) 10 0,1 8:(0,2) 9:(0,3) 10:(0,1) 12 0,0 11:(0,2) 目标状态
|| ( R==1 )&&( G==1 ) || ( R==2 )&&( G==2 ) );
第1类安全点 第2类安全点
第3类安全点 ( 人数=鬼数 )
安全渡河的关键点 3 个 人 2 都 在 1 西 岸
G 3 0,3
0,2
1,3 1,2
2,3 2,2 2,1
3,3 3,2 3,1
0,1 1,1
0,0
v=mv; //存东岸鬼数 s[k+1].r=u; //存东岸人数 s[k+1].g=v; //存东岸鬼数 if(u==1&&v==1) flag1++; //给达到1人1鬼的状态加标志 if(u==2&&v==2) flag2++; //给达到2人2鬼的状态加标志 break; }//if }//for结束 }//while循环体结束
2,3 2,2 2,1
3,3 3,2 3,1
0,0
0
1,0 1
2,0 2
3,0 3
R
从东岸状态图分析有3类点是安全的: 第1类:3个人在东岸未动,有4个点 第2类:3个人已不在东岸,有4个点 第3类:人数和鬼数相等,只有两个点 ( 1,1 )-----1人1鬼 ( 2,2 )-----2人2鬼 安全渡河要从第1类的4个点转到第2类的4个 点,必须经过第3类的2个点。下图画出了这 10个点。
G 3 0,3
2 0,2 1 0,1
1,3 1,2 1,1
2 2,3
2,2 2,1
3,3 3,2 3,1
1
3
0,0
0
1,0 1
2,0 2
3,0 3
R
第4步:1鬼上船东渡,东岸有3人1鬼
G 3 0,3
2 0,2 1 0,1
2
1,3 1,2 1,1 2,3 2,2 2,1 3,3 3,2 3,1 3 1
2,3 2,2 2,1
3,3 3,2 3,1
1
0,0
0
1,0 1
2,0 2
3,0 3
R
第2步:1鬼返回,东岸有3人2鬼
G 3 0,3
2 0,2 1 0,1
2
1,3 1,2 1,1 2,3 2,2 2,1 3,3 3,2 3,1 1
0,0
0
1,0 1
2,0 2
3,0 3 R
第3步:2鬼上船西渡,东岸有3人
摆渡决策用结构数组来描述 Int d[7]={ { 0, 0},{0,-2},{-1,-1},{-2,0 }, { 0,1},{1,1},{1,0 } }; i-------决策号 从东往西 从西往东 i 0 1 2 3 4 5 6 d[i].r 0 0 -1 -2 0 1 1 人数 d[i].g 0 -2 -1 0 1 1 0 鬼数
摆渡决策
定义2维向量 dk为第k次行船的摆渡策略。 dk = ( uk, vk )。 其中uk为上船人数, vk为上船鬼数。 允许的摆渡策略集合为D
D= { (u ,v)| u=2, v=0; u=1, v=0; u=1, v=1; u=0, v=1; u=0,v=2; }
状态转移公式
SK+1=SK+(-1)kdk k为偶数 +dk SK -dk k为奇数 SK+1
int main() { s[1].r=3; s[1].g=3; int u=3,v=3; int a,mu,mv;
// 主函数 //主函数开始 //初始状态东岸有3人 //初始状态东岸有3鬼 //初始状态东岸有3人3鬼 //中间变量
while(!(u==0 && v==0)) //目标是东岸既无人又无鬼,未达目标循环不已 {//while循环体开始 k=k+1; //状态号加1 if(k%2==0)a=4; //k为偶数表示船东渡,策略号从4开始 else a=1; //k为奇数表示船西渡,策略号从1开始 for(int i=a;i<=a+2;i++) //循环 //试探采用哪个决策能安全走1步 {//for开始 mu=u+d[i].r; //按第i号策略走1步东岸的人数 mv=v+d[i].g; //按第i号策略走1步东岸的鬼数
3,0 3
R
东岸状态图
多步决策的出发点 2,3 2,2 2,1 3,3 3,2 3,1
G 3 0,3
2 0,2
1,3 1,2 1,1
1 0,1
多步 决策 的结 束点
0,0
0
1,0 1
2,0 2
3,0 3
R
第1步:2鬼上船西渡,东岸留3人1鬼
G 3 0,3
2 0,2 1 0,1
1,3 1,2 1,1
3,3 3,2 3,1
0,0
1,0
1
2,0
2
3,0
3 R
0
人数与鬼数相等的2个点
G 3 0,3
2 1 0,2 0,1
1,3 1,2 1,1
2,3 2,2 2,1
3,3 3,2 3,1
0,0
0
1,0 1
2,0 2
3,0 3
R
安全的10个点 3 个 人 2 都 在 1 西 岸
人数与鬼数相等 1,3 1,2 1,1 2,3 2,2 2,1 3,3 3,2 3,1 3 个 人 都 在 东 岸 R
8 G 7 9 3 0,3 1,3
2 1,2
6
2,3 2,2 2,1
2
3,2 3,1 3 4 R
1 0,1 1,1 10 11 终止 0
1 5
2
3,0 4 3
编程思路:多步决策,使用状态转移方程 (3,3) d1 d2 d3 s1 s2 s3 从东往西 从西往东 从东往西
dn-1 dn
sn-1 sn sn+1 (0,0) 从西往东 从东往西
6
1,3 1,2 2,3 2,2 2,1
2
3,3 3,2 3,1 3 1
1 0,1 1,1
0,0
0 1 5 2
3,0 4 3
R
第7步:2人上船西渡,东岸有2鬼
G 7 3 0,3 1,3
2 1,2
6
2,3 2,Байду номын сангаас 2,1
2
3,3 3,2 3,1 3 1
1 0,1 1,1
0,0
0 1 5 2 4
3,0 3
参考程序
#include<iostream> using namespace std; //定义布尔变量,描述状态安全和重复 bool AQ,CHF; void display(int k); //声明有输出渡河状态函数 //定义描述渡河状态东岸人数 与鬼数的结构变量 struct state { int r; //状态中的人数 int g; //状态中的鬼数 };
state s[15]; //结构数组记录渡河时的状态转移过程
state d[7]={{0,0},{0,-2},{-1,-1}, {- 2,0},{0,1},{1,1},{1,0}}; //渡河策略 int flag1=0; //东岸尚未达1人1鬼状态标志预置0 int flag2=0; //东岸尚未达2人2鬼状态标志预置0 int k=0; //状态号预置0
多步决策问题
安全渡河方案为多步决策问题 目标:求决策 dk ∈ D (k=1,2,…n),使状态 Sk ∈ S , 按照状态转移公式 SK+1=SK+(-1)kdk 由初始状态S1=( 3,3 ) 经有限步骤n到达 Sn+1 =( 0,0 )
东岸状态图
G 3 0,3
2 0,2 1 0,1
1,3 1,2 1,1
G 3 0,3
0,2 0,1
0,0
1,0
1
2,0
2
3,0
3
0
不安全的6个点: 人数为1或2时与鬼数不等
G 3 0,3
2 1 0,2 0,1
1,3 1,2 1,1
2,3 2,2 2,1
3,3 3,2 3,1
0,0
0
1,0 1
2,0 2
3,0 3
R
将安全条件形式化
AQ= ( R==3 || R==0
12:(0,0)
3 3,2
6 1,1
5
2 3,1
4 3,0
结论: 1,这是多步决策的最佳方案; 2,使用状态转移方程; 3,排除重复才能提高搜索效率; 4,理性思维极为重要; 5,学会计算思维,数学建模。
讨论: 1,4人4鬼过河问题该如何思考? 2,3人3鬼过河问题还有其他解法吗? 3,上述程序只能得出一个渡河方案, 你能得出所有的方案吗?