数学建模:商人过河问题(牛虎问题/传教士野人问题)C语言实现

在这里插入图片描述

问题分析

读题可以看出这是一个多步决策问题,可以用计算机进行编程解决更大范围的问题,建模时老师可能会要求用matlab进行编程实现,但我还是喜欢用C语言,可以更加直观地显示建模过程!!!

对于每一步的决策,可以选择一定数量的商人和仆人上船,然后在河的左岸和右岸之间进行摆渡,并且保证每一次摆渡都不能使得商人被杀死(当然,如果不存在一种安全过河的方案,那么商人必死)

当n=3,r=2时,船的左岸或右岸的(商人,仆人)数目有4*4=16种选择,再加上岸的左右,所以有32种选择。然后再去考虑船上(商人,仆人)的数目组合(注意:船上肯定不能没有人!)
对于给定的所有穿上(商人,仆人)的组合,从(n,n)这个初始状态开始,进行不断的选择操作,然后如果下一个(商人,仆人)状态符合要求,则保存到相应的栈数组中,直到左岸(商人,仆人)=(0,0),且船在右岸。

注:上述提到的(商人,仆人)均为左岸的商人、仆人数目,因为商人和仆人的人数已经给定,所以一旦左岸人数确定,那么右岸人数必然已经确定了。
其实本题的思路就是朴素的深度优先搜索,针对一种过河方案,一鼓作气走到底,如果不能成功,则选择下一个过河方案。是回溯法的应用!

AC代码

/*
Author:snnu_lgw
Date:2020/6/11 
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;


int state[100][100][2];  /*商人和仆人的状态是否已经访问过,0表示未访问,1表示已经访问过,
                         **数组的第一维表示商人的数目,第二维表示仆人的数目,第三维表示左岸或者右岸:0是左岸,1是右岸*/ 
int state_num ; //已经存在的状态的个数 
int n,r;/*n表示商人/仆人的个数,r表示小船乘坐的人数*/
ll num_ship; /*表示船上 人数的状态 数目 */
int OK;/*判断商人是否能够成功过河*/ 

struct Ship{
	int a; //船上 商人的数目 
	int b; //船上 仆人的数目 
}ship[100000]; //可以运输的商人与仆人的数目 

struct Node{
	int a,b;
	int c; //0表示在左岸,1表示在右岸 
}node[10000]; /*存储所有走过的状态*/

void init_ship()
{/* 初始化合适的船上的人数的集合 */
	int num = 1;
	while(num<=r)
	{
		ship[++num_ship].a=0;
		ship[num_ship].b=num; 
		num++;
	}
	
	for(int i=1;i<=r;i++)
	{	
		int t = 0;
		while(t<=i && (t+i)<=r)
		{
			ship[++num_ship].a=i;
			ship[num_ship].b=t;
			t++;
		}
	} 
}


void CrossRiver(int x,int y,int z)
{
	node[++state_num].a = x;
	node[state_num].b = y;
	node[state_num].c = z; 
	
	if(x==0&&y==0&&z==1)
		{
			OK = 1;
			for(int i=1;i<=state_num-1;i++) 
				cout<<"("<<node[i].a<<","<<node[i].b<<")"<<"--->"<<"("<<node[i+1].a<<","<<node[i+1].b<<")"<<endl;
			return;
		}
	else 
		{
			for(int i=1;i<=num_ship;i++)
			{
				if(z==0)/*船当前在左岸,那么下一步船将开往右岸*/
				{
					int left_x = x-ship[i].a;
					int left_y = y-ship[i].b;
					int right_x = n-left_x;
					int right_y = n-left_y; 
					
					if( (left_x>=0&&left_y>=0)&&   //保证左岸的商人和仆人的数量没有减为 负数 ,因为船是往右岸方向走,所以右岸数目不会变为负数(毕竟船不会空载) 
						(left_x>=left_y||left_x==0)&&   // 保证左岸的商人数目大于等于仆人数目,或者商人数目为0 
						(right_x>=right_y||right_x==0)&&// 保证右岸的商人数目大于等于仆人数目,或者商人数目为0 
						(state[left_x][left_y][1]==0)  //保证即将到达的下一个状态是没有访问过的 
					  )
					{
						state[left_x][left_y][1] = 1;//当前状态访问了! 
						CrossRiver(left_x,left_y,1);	
					} 
				}
				else /*船当前在右岸,那么下一步船将开往左岸*/
				{
					int left_x = x+ship[i].a;
					int left_y = y+ship[i].b;
					int right_x = n-left_x;
					int right_y = n-left_y; 
					
					if( (right_x>=0&&right_y>=0)&&   //保证右岸的商人和仆人的数量没有减为 负数 
						(left_x>=left_y||left_x==0)&&   // 保证左岸的商人数目大于等于仆人数目,或者商人数目为0 
						(right_x>=right_y||right_x==0)&&// 保证右岸的商人数目大于等于仆人数目,或者商人数目为0 
						(state[left_x][left_y][0]==0)  //保证即将到达的下一个状态是没有访问过的 
					  )
					{
						state[left_x][left_y][0] = 1;//当前状态访问了! 
						CrossRiver(left_x,left_y,0);	
					} 
				}
			}
		}
	
	state[x][y][z] = 0; //该状态改为 未访问!  
	state_num--;  //状态出栈,理论上的出栈,实际内存上没有 
	
}

int main()
{
	cin>>n>>r;
	init_ship();
/*
**输出所有可以满足条件的船上 商人&仆人的个数  
	for(int i=1;i<=num_ship;i++)
		cout<<ship[i].a<<" "<<ship[i].b<<endl; 
*/
	state[n][n][0] = 1;//初始状态访问 
	CrossRiver(n,n,0);  //初始0表示船在左岸 
	if(OK==0) 
		cout<<"Game Over!\n"<<n<<"个商人,"<<n<<"个仆人,船上可乘坐"<<r<<"个人的情况下无法成功将商人运送到河对岸!"<<endl;
	return 0;
}

参考资料:https://www.cnblogs.com/kangjianwei101/p/5221654.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值