C++习题:野人与修道士过河问题

原创 2013年12月03日 08:53:54
 
河的左岸有3个野人和3个修道士以及一条小船,修道士们想用这条小船把所有的人都运到河的右岸,但又受到以下限制:
1、修道士和野人都会划船,但船一次只能载2人;
2、在任何岸边,野人数不能超过修道士数,否则修道士将会被野人吃掉。
假定野人愿意服从任何一种过河的安排,请规划出一种确保修道士安全的过河方案。

以下是犯规情形: - 例如,对面有1个坏,船上有1好1坏,则船到对面后1好2坏,犯规。

 
#include <stdio.h>

struct record
{
	int a,b,c,d;
};

record history[1000];
int    count = 0;

// 保存记录
void Push(int a, int b, int c, int d)
{
	record* r = &history[count];
	r->a = a;
	r->b = b;
	r->c = c;
	r->d = d;
	count ++;
}

// 追溯
void Pop()
{
	count --;
}

// 压入一个相对值,表示每次运人的数目
//(2,0)(0,2)(1,1)(1,0),(0,1)
//(-2,0)(0,-2)(-1,-1)(-1,0),(0,-1)
void Push(int x1, int x2)
{
	record* r = &history[count-1];
	int a = r->a;
	int b = r->b;
	int c = r->c;
	int d = r->d;
	Push(a-x1, b-x2, c+x1, d+x2);
}

// 当前状态是否曾经出现过,避免无限循环
bool IsDuplicate()
{
	record* last = &history[count-1];

	// 取最后一条
	for(int i=0; i<count-1; i++)
	{
		record* r = &history[i];
		if(last->a == r->a && last->b == r->b)
		{
			if(i%2 == (count-1)%2)
				return true;
		}
	}
	return false;
}

// 辅助打印函数
void PrintHistory()
{
	printf("\n---------------\n");
	for(int i=0; i<count; i++)
	{
		record* r = & history[i];

		if(i%2) printf(">> ");
		else    printf("<< ");
		printf("%d %d %d %d\n", r->a, r->b, r->c, r->d);
	}
}

// count: 已经存在的记录数
bool TryPlan()
{
	// PrintHistory();

	// 取得最后一条记录
	record* r = &history[count-1];
	int a = r->a;
	int b = r->b;
	int c = r->c;
	int d = r->d;

	// 已经到达最终状态
	if(a==0 && b==0 && c==3 && d==3)
		return true;

	// 是否有效
	if(a<0 || b<0 || c<0 || d<0) 
	{
		printf("invalid.\n");
		return false;
	}

	// 检查此状态是否安全
	if( a>0 && a<b) 
	{
		printf("not safe.\n");
		return false;
	}
	if( c>0 && c<d) 
	{
		printf("not safe.\n");
		return false;
	}

	// 检查此状态是否已经出现过
	if( IsDuplicate()) 
	{
		printf("dup\n");
		return false;
	}

	printf("Continued.\n");

	if(count % 2 != 0) 
	{
		// 偶数: 从本岸到对岸,过去
		Push(0,2);
		if(TryPlan()) return true;
		Pop();

		Push(2,0);
		if(TryPlan()) return true;
		Pop();

		Push(1,1);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(1,0);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(0,1);
		if(TryPlan()) return true;
		Pop();

		return false;
	}
	else
	{
		// 奇数: 从对岸对本岸,回来
		Push(-2,0);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(0,-2);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(-1,-1);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(-1,0);
		if(TryPlan()) return true;
		Pop(); // 退回一层

		Push(0,-1);
		if(TryPlan()) return true;
		Pop();

		return false;

	}
}

int main()
{
	Push(3,3,0,0);
	bool ret = TryPlan();
	if(! ret)
	{
		printf("没有解决方案");
	}
	else
	{
		for(int i=0; i<count; i++)
		{
			record* r = & history[i];
			printf("%d %d %d %d\n", r->a, r->b, r->c, r->d);
		}
	}

	return 0;

}
 

答案:

3 3 0 0

3 1 0 2

3 2 0 1

3 0 0 3

3 1 0 2

1 1 2 2

2 2 1 1

0 2 3 1

0 3 3 0

0 1 3 2

1 1 2 2

0 0 3 3

 

相关文章推荐

修道士与野人问题

1、问题描述:这是一个古典问题.假设有n个道士和n个野人准备渡河.但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的个数(除非修道士个数为0).如果两种人都...
  • kilvdn
  • kilvdn
  • 2006年07月05日 19:36
  • 6777

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

prolog学习_修道士野人问题

只贴代码跟样例输出:move(1,0).%表示船上有一位牧师,没有野人。 move(0,1). move(0,2). move(2,0). move(1,1). legal((X,Y,_))...

请教“修道士与野人问题”

有N个修道士和N个野人准备渡河,但只有一条能容纳C人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数(除非修道士的个数为0)。两种人都会划船,设计一算法,确定他们能否过河...

2.修道士和野人问题

2.修道士和野人问题:设有三个修道士和3个野人来到河边,打算用一条船从河的左岸渡到河的右岸去。但该船每次只能装载两个人,在任何岸边野人的数目都不得超过修道士的人数,否则修道士就会被野人吃掉。假设野人服...

修道士和野人过河问题 A*算法 人工智能

/** * 2014-08-25 by Liy * 修道士和野人过河问题,一共有3个修道士和3个野人,1条船 * 1、船最多可乘坐2人 * 2、两岸边 野人的数量不能多于修道士的数量,...

修道士与野人问题——C++源代码,伪代码,详细分析

前言:这一个经典的问题,可以把问题转换成数据结构中的 图 来解决。

修道士和野人

import java.util.*; import java.io.*; public class MACPS { final public class SolutionNotFoundE...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++习题:野人与修道士过河问题
举报原因:
原因补充:

(最多只允许输入30个字)