问题分析
读题可以看出这是一个多步决策问题,可以用计算机进行编程解决更大范围的问题,建模时老师可能会要求用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;
}