数学模型 商人过河问题 C++实现

数学模型 商人过河问题 C++实现

问题描述

​ 将过河问题抽象为一个数学问题,安全渡河即为一个多步决策问题,在安全的前提下,每一步都考虑船上的商人与随从人数情况。

​ 决策问题通常从考虑状态,决策,状态转移方程入手。

状态

​ 设 s k = ( x k , y k ) s_k=(x_k,y_k) sk=(xk,yk)表示第 k k k次渡河前此岸的商人数与随从数,显然有 x k , y k = 0 , 1 , 2 , 3 ; k = 1 , 2 , ⋯ x_k,y_k=0,1,2,3;k=1,2,\cdots xk,yk=0,1,2,3;k=1,2,

​ 考虑安全的状态集合即 S = { ( x , y ) ∣ x = 0 , y = 0 , 1 , 2 , 3 ; x = 3 , y = 0 , 1 , 2 , 3 ; x = y = 1 , 2 } S=\{(x,y)|x=0,y=0,1,2,3;x=3,y=0,1,2,3;x=y=1,2\} S={(x,y)x=0,y=0,1,2,3;x=3,y=0,1,2,3;x=y=1,2}其中第一项表示商人全部在对岸,此岸无商人,故无法抢劫;第二项表示商人全部在此案,对岸无商人,亦无法抢劫;第三项表示两岸的商人与随从相等。

决策

​ 设 d k = ( Δ x k , Δ y k ) d_k=(\Delta{x_k},\Delta{y_k}) dk=(Δxk,Δyk)表示第 k k k次渡河时船上的商人数与随从数,显然有 Δ x k , Δ y k = 0 , 1 , 2 ; k = 1 , 2 , ⋯ \Delta{x_k},\Delta{y_k}=0,1,2;k=1,2,\cdots Δxk,Δyk=0,1,2;k=1,2,

​ 考虑允许的决策集合 D = { ( x , y ) ∣ x , y = 0 , 1 , 2 ; x + y = 1 , 2 } D=\{(x,y)|x,y=0,1,2;x+y=1,2\} D={(x,y)x,y=0,1,2;x+y=1,2}。故有五种不同的决策: ( 0 , 1 ) , ( 0 , 2 ) , ( 1 , 0 ) , ( 1 , 1 ) , ( 2 , 0 ) (0,1),(0,2),(1,0),(1,1),(2,0) (0,1),(0,2),(1,0),(1,1),(2,0)

状态转移方程

​ 即求解 s k + 1 = f ( s k , d k ) s_{k+1}=f(s_k,d_k) sk+1=f(sk,dk)
当为奇数次划船时为从此岸到彼岸,故 s k + 1 = s k − d k s_{k+1}=s_k-d_k sk+1=skdk
当为偶数次划船时为从彼岸至此案,故 s k + 1 = s k + d k s_{k+1}=s_k+d_k sk+1=sk+dk
故状态转移方程为:
s k + 1 = s k + ( − 1 ) k d k k = 1 , 2 , ⋯ s_{k+1}=s_k+(-1)^kd_k\quad k=1,2,\cdots sk+1=sk+(1)kdkk=1,2,故问题即为:求一系列 d k ∈ D ( k = 1 , 2 , ⋯   , n ) d_k\in{D}(k=1,2,\cdots,n) dkD(k=1,2,,n),使 s k ∈ S s_k\in{S} skS且由 s 1 = ( 3 , 3 ) s_1=(3,3) s1=(3,3)转移至 s n + 1 = ( 0 , 0 ) s_{n+1}=(0,0) sn+1=(0,0)

​ 其中安全状态点集如下图绿点所示:
安全状态集合

图片取自姜启源老师课件

求解

​ 此代码仅能模拟两组基础解(即11步的解),剩下两组可以根据对称性得出。

#include <iostream>
#include <vector>
using namespace std;
bool check[4][4]={	/*状态是否安全*/
	{1,1,1,1},
	{0,1,0,0},
	{0,0,1,0},
	{1,1,1,1}	
};
int d[5][2] = {{0,1},{0,2},{1,0},{1,1},{2,0}};	/*决策*/
struct S
{
	int x;
	int y;
};
vector<S> v;	/*记录路径*/
int count = 0;
int k = 1;
void ouput(){
	for(int i=0; i<v.size(); i++){
		cout<<i+1<<": "<<v[i].x<<" "<<v[i].y<<endl;
	}
	cout<<endl;
}
bool valid(int x, int y){	/*状态是否合法*/
	return (0<=x && x<=3) && (0<=y && y<=3);
}
void solve(int x, int y, int way){	/*dfs函数*/
	if(count == 5)	/*只取前五种*/
		return;
	if(x==0 && y==0){	/*递归基*/
		ouput();
		count++;
		return;
	}
	for(int i=0; i<5; i++){	/*5种不同决策*/
		if(k%2==0){
			x += d[i][0];
			y += d[i][1];
		}else{
			x -= d[i][0];
			y -= d[i][1];
		}
		if(valid(x,y)){
			if(check[x][y] && way != i){	/*新状态安全且不等于原状态*/
				S s;
				s.x = x;s.y = y;
				v.push_back(s);
				k++;
				solve(x, y, i);		/*向下层搜索*/	
				//if(count % 2 == 0)
					//i++;
				v.pop_back();	/*置为原状态*/
				k--;
				if(k%2){
					x += d[i][0];
					y += d[i][1];
				}else{
					x -= d[i][0];
					y -= d[i][1];
				}
			}else{	/*新状态不能选*/
				if(k%2){
					x += d[i][0];
					y += d[i][1];
				}else{
					x -= d[i][0];
					y -= d[i][1];
				}
			}
		}else{		/*新状态不合法,置为原状态*/
			if(k%2){
			x += d[i][0];
			y += d[i][1];
			}else{
				x -= d[i][0];
				y -= d[i][1];
			}
		}
	}
}
int main(){
	solve(3,3,-1);
	cout<<count<<endl;
	return 0;
}

​ 结果:

在这里插入图片描述在这里插入图片描述在这里插入图片描述
111519

​ 可以看到,在11步时,有两种情况,即其后半部分有两种情况,如下:
( 0 , 2 ) → ( 0 , 3 ) → ( 0 , 1 ) → ( 0 , 2 ) → ( 0 , 0 ) ( 0 , 2 ) → ( 0 , 3 ) → ( 0 , 1 ) → ( 1 , 1 ) → ( 0 , 0 ) (0,2)\to(0,3)\to(0,1)\to(0,2)\to(0,0)\\ (0,2)\to(0,3)\to(0,1)\to(1,1)\to(0,0) (0,2)(0,3)(0,1)(0,2)(0,0)(0,2)(0,3)(0,1)(1,1)(0,0)而在步数增加时,会形成局部的循环,如紫色框所示,故在前半部分决策时只能模拟一种情况,即
( 3 , 3 ) → ( 3 , 1 ) → ( 3 , 2 ) → ( 3 , 0 ) → ( 3 , 1 ) (3,3)\to(3,1)\to(3,2)\to(3,0)\to(3,1) (3,3)(3,1)(3,2)(3,0)(3,1)根据对称性即式 ( 2 ) (2) (2),不难得出前半部分另一种决策为:
( 3 , 3 ) → ( 2 , 2 ) → ( 3 , 2 ) → ( 3 , 0 ) → ( 3 , 1 ) (3,3)\to(2,2)\to(3,2)\to(3,0)\to(3,1) (3,3)(2,2)(3,2)(3,0)(3,1)故基础的决策情况即11步时,有4组基本解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值