修道士和野人问题

题目:

设在河的一岸有三个野人、三个修道士和一条船,修道士想用这条船把所有的人运到河对岸,但受以下条件的约束:

一是修道士和野人都会划船,但每次船上至多可载两个人;
二是在河的任一岸,如果野人数目超过修道士数,修道士会被野人吃掉。

如果野人会服从任何一次过河安排,请规划一个确保修道士和野人都能过河,且没有修道士被野人吃掉的安全过河计划。

代码一(暴力搜索):

#include <iostream>
#include <vector>
#include <stdio.h>
#include <string>

using namespace std;

vector<string> visit;

int numofchurch, numofwild, boatload;     //分别表示传教士总人数,野人总人数,船载客量
int totalanswer = 0;                      //解法数目

bool check(int M, int C, int flag)
{
	if ((flag != 0) && (flag != 1))
		return false;
	if (M<0 || C<0 || numofchurch - M<0 || numofwild - C<0)           //人数不得小于0
		return false;
	if ((M&&M<C) || ((numofchurch - M) && (numofchurch - M)<(numofwild - C)))               //任何一岸有传教士且传教士人数小于野人人数
		return false;
	char s[30];
	sprintf(s, "M=%d, C=%d, flag=%d", M, C, flag);
	for (int i = 0; i<visit.size(); i++)          //此情况之前出现过
	if (s == visit[i])
		return false;
	visit.push_back(s);
	return true;
}

void path(int M, int C, int flag)
{
	if ((M == 0) && (C == 0) && (flag == 0))
	{
		totalanswer++;
		cout << "第" << totalanswer << "种方法:" << endl << endl;
		for (int i = 0; i<visit.size(); i++)
			cout << visit[i] << endl << endl;
		return;
	}

	int i, j, k;
	for (i = boatload; i>0; i--)          //船本次载了i个人
	{
		for (j = i; j >= 0; j--)        //j为运送的传教士人数
		{
			k = i - j;                 //k为运送的野人数
			if (check(M - j, C - k, flag - 1))
			{
				path(M - j, C - k, flag - 1);
				visit.pop_back();
			}
			else if (check(M + j, C + k, flag + 1))
			{
				path(M + j, C + k, flag + 1);
				visit.pop_back();


			}
		}
	}
}


int main()
{
	int flag = 1;                         //flag=1表示船在左岸
	char s[30];
	cout << "输入传教士数、野人数和船载客量(三个数字以空格间隔):" << endl;
	cin >> numofchurch >> numofwild >> boatload;
	sprintf(s, "M=%d, C=%d, flag=%d", numofchurch, numofwild, flag);
	visit.push_back(s);
	path(numofchurch, numofwild, flag);
	if (totalanswer != 0)
		cout << "Total answer: " << totalanswer << endl;
	else
		cout << "FAIL!" << endl;
	system("pause");
}

代码二(A*算法):

#include <iostream> 
#include <vector>
#include <cmath>

using namespace std;

int X, Y;
int k;

struct node
{
	int q[3];
};

vector<node> s;
int q[500][3];
//用于存放搜索结点,q[][0]是左岸传教士人数
//q[][1]是左岸野蛮人人数,q[][2]是左岸船的数目
//q[][3]用于搜索中的父亲结点序号。
int ans = 0;

int op_num = 0;
int go[500][2];
int fx[500][500];

//安全状态:左岸中,传教士都在or都不在or传教士人数等于野人人数 
int is_safe(int state[3])
{
	if ((state[0] == 0 || state[0] == X || state[0] == state[1]) && (state[1] >= 0) && (state[1] <= Y))
	{
		return 1;
	}
	return 0;
}

//是否到达目标状态 
int is_success(int state[3])
{
	if (state[0] == 0 && state[1] == 0)
		return 1;
	return 0;
}

//该状态是否已经访问过 
int vis(int state[3])
{
	for (vector<node>::iterator it = s.begin(); it != s.end(); it++)
		if ((*it).q[0] == state[0] && (*it).q[1] == state[1] && (*it).q[2] == state[2])
			return 1;
	return 0;
}


int f2(int state[3])
{
	return state[0] + state[1] - k * state[2];
}


int find_min(int cur)
{
	int min = 10000;
	int op = -1;
	for (int j = 0; j < op_num; j++)//分别考虑可能的动作
	{
		if (fx[cur + 1][j] < min)
		{
			min = fx[cur + 1][j];
			op = j;
		}
	}
	if (min == 10000)
		op = -1;
	return op;
}


//过河操作
int search(int cur)
{
	if (is_success(q[cur]))
	{
		ans = cur;
		return 1;
	}
	int state[3];
	int j;
	//cout<<"第"<<cur<<"层搜索"<<endl;
	//获取当前搜索结点
	//cout<<"展开结点"<<cur<<":"<<q[cur][0]<<' '<<q[cur][1]<<' '<<q[cur][2]<<endl;
	if (q[cur][2])//船在左边
	{
		for (j = 0; j < op_num; j++)//分别考虑可能的动作
		{
			state[0] = q[cur][0] - go[j][0];
			state[1] = q[cur][1] - go[j][1];
			state[2] = 0;//船到了右边
			fx[cur + 1][j] = f2(state);
		}
		j = find_min(cur);
		while (j != -1)
		{
			fx[cur + 1][j] = 10000;
			state[0] = q[cur][0] - go[j][0];
			state[1] = q[cur][1] - go[j][1];
			state[2] = 0;//船到了右边
			if (is_safe(state) && !vis(state))//如果是安全状态//判断与之前展开结点是否相同
			{
				node nd;
				nd.q[0] = q[cur + 1][0] = state[0];
				nd.q[1] = q[cur + 1][1] = state[1];
				nd.q[2] = q[cur + 1][2] = state[2];
				s.push_back(nd);
				//cout<<"合法结点:"<<state[0]<<' '<<state[1]<<' '<<state[2]<<endl;       
				if (search(cur + 1))
					return 1;
			}
			j = find_min(cur);
		}
	}
	else    //船在右边
	{
		for (j = 0; j < op_num; j++)//分别考虑可能的动作
		{
			state[0] = q[cur][0] + go[j][0];
			state[1] = q[cur][1] + go[j][1];
			state[2] = 1;
			fx[cur + 1][j] = f2(state);
		}
		j = find_min(cur);
		while (j != -1)
		{
			fx[cur + 1][j] = 10000;
			state[0] = q[cur][0] + go[j][0];
			state[1] = q[cur][1] + go[j][1];
			state[2] = 1; //船回到左边
			if (is_safe(state) && !vis(state))//如果是安全状态且与之间状态不同
			{
				node nd;
				nd.q[0] = q[cur + 1][0] = state[0];
				nd.q[1] = q[cur + 1][1] = state[1];
				nd.q[2] = q[cur + 1][2] = state[2];
				s.push_back(nd);
				//cout<<"合法结点:"<<state[0]<<' '<<state[1]<<' '<<state[2]<<endl;
				if (search(cur + 1))
					return 1;
			}
			j = find_min(cur);
		}
	}
	return 0;
}

int main()
{
	int n;
	cout << "请输入N:";
	cin >> n;
	cout << "请输入k:";
	cin >> k;
	X = Y = n;

	int state[3];
	//初始状态 
	node nd;
	nd.q[0] = state[0] = q[0][0] = X;
	nd.q[1] = state[1] = q[0][1] = Y;
	nd.q[2] = state[2] = q[0][2] = 1;

	s.push_back(nd);
	//初始化操作
	cout << "合法的操作组有:" << endl;
	for (int i = 1; i <= k; i++)
		for (int j = 0; j <= i; j++)
		{
			if (j >= i - j || j == 0)
			{
				go[op_num][0] = j;
				go[op_num][1] = i - j;
				cout << go[op_num][0] << ' ' << go[op_num][1] << endl;
				op_num++;
			}
		}
	cout << endl;
	if (!search(0))
	{
		cout << "无解" << endl;
		return 0;
	}
	cout << "找到的解为:" << endl;
	for (int i = 0; i <= ans; i++)
	{
		//cout<<q[i][0]<<' '<<q[i][1]<<' '<<q[i][2]<<endl;
		if (i > 0)
		{
			cout << abs(q[i][0] - q[i - 1][0]) << "个传教士和" << abs(q[i][1] - q[i - 1][1]) << "个野人";
			if (q[i][2])
				cout << "从右岸乘船至左岸" << endl;
			else
				cout << "从左岸乘船至右岸" << endl;
			cout << "左岸有" << q[i][0] << "个传教士和" << q[i][1] << "个野人" << endl;
			cout << "右岸有" << n - q[i][0] << "个传教士和" << n - q[i][1] << "个野人" << endl << endl;
		}
	}

	cout << "本次搜索所花费的费用:" << ans << endl;
	system("pause");
	return 0;
}
  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个用C语言实现的n个道士和n个野人坐船过河问题的程序: ```c #include <stdio.h> #define MAX 10 int left_m, left_c, boat_m, boat_c, right_m, right_c; int num_m, num_c; void print_state() { printf("Left: %d %d, Boat: %d %d, Right: %d %d\n", left_m, left_c, boat_m, boat_c, right_m, right_c); } int valid() { if (left_m < 0 || left_c < 0 || right_m < 0 || right_c < 0) { return 0; } if (left_m > 0 && left_m < left_c) { return 0; } if (right_m > 0 && right_m < right_c) { return 0; } return 1; } void move(int m, int c) { boat_m -= m; boat_c -= c; right_m += m; right_c += c; } void back(int m, int c) { boat_m += m; boat_c += c; right_m -= m; right_c -= c; } void search() { if (boat_m > 2 || boat_c > 2) { return; } if (right_m == num_m && right_c == num_c) { print_state(); return; } if (valid()) { print_state(); move(2, 0); search(); back(2, 0); move(0, 2); search(); back(0, 2); move(1, 1); search(); back(1, 1); move(1, 0); search(); back(1, 0); move(0, 1); search(); back(0, 1); } } int main() { printf("Enter the number of missionaries and cannibals: "); scanf("%d", &num_m); num_c = num_m; left_m = num_m; left_c = num_c; boat_m = 0; boat_c = 0; right_m = 0; right_c = 0; search(); return 0; } ``` 该程序使用了递归的方法来搜索所有可能的移动方案,并在每次移动之后检查当前状态是否满足条件。如果满足条件,则输出当前状态并继续搜索下一个状态。如果不满足条件,则需要返回之前的状态并尝试其他移动。最终,程序将输出所有可以让所有人都安全过河的方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值