计算思维_第一周_人鬼渡河

一.题目描述

1.一次最多两人(2人/2归/1人+1鬼)
2.无论在河哪边,鬼比人多就会把人吃掉(船靠岸时人数船和岸一起算)
求一个解决方案

二.解决方案

要找到渡河方案,即一系列“指令”
对于计算机而言,“指令”就是对“数”的运算,因此要确定哪些“数”需要被算
考察本题,可算的量为西岸,船上,东岸的人鬼数量的变化
、那么,设东岸人数R,鬼数G,用(R,G)表示,可以构成如下的4x4网络
在这里插入图片描述
要从(3,3)移动到(0,0)
状态转移等式: S K + 1 = S k + δ k S_K+1=S_k+\delta_k SK+1=Sk+δk
设k为渡船航行次数,k为奇,东向西,k为偶,西向东
d_k=(U_k,V_k)为第k次的渡河策略,
策略集合为
D = { (U,V) | U = 2, V = 0;
U = 1, V = 0;
U = 1, V = 1;
U = 0, V = 1;
U = 0, V = 2;
}
k=(R_k,G_k)为第k次东岸的安全状态(人不会被吃)(同时考虑东岸和西岸的情况)
安全状态集合S
S = { (R,G) | R = 0, G = 0, 1, 2, 3;
R = 3, G = 0, 1, 2, 3;
R = 1, G = 1;
R = 2, G = 2;
}
因此东岸有0人或3人或人鬼数量相等时为安全状态
状态转移公式: S k + 1 = S k + ( − 1 ) k ∗ d k S_{k+1}=S_k+(-1)^k*d_k Sk+1=Sk+(1)kdk

三.代码

#include<iostream>
#include<iomanip>
using namespace std;
struct state {
	int R, G;
};//东岸的人数与鬼数
state s[20];//记录渡河时的状态转移过程
int choice[20] = { 0 };//决策记录
int k;//渡河步数

state d[6] = { {0,0},{1,1},{0,1},{2,0},{1,0},{0,2} };

void display();
void transfer_state();
int main()
{
	transfer_state();
	display();
	return 0;
}

void transfer_state() {
	k = 1;
	s[1].R = 3;
	s[1].G = 3;
	do {
		int fx = 1;//渡船方向
		if (k % 2 == 1)
			fx = -1;
		int i;
		for (i = choice[k + 1] + 1; i <= 5; i++)//???
		{
			int u = s[k].R + fx * d[i].R;//按策略i走后东岸人数
			int v = s[k].G + fx * d[i].G;
			if (u > 3 || v > 3 || u < 0 || v < 0)
				continue;//判断是否越界
			if (!(u == 3 || u == 0 || u == v))
				continue;//判断是否安全
			bool repeat = false;//用于判断是否重复
			for (int j = k - 1; j >= 1; j -= 2)//从k-1开始查,仅考虑同方向故j-=2
			{
				if (s[j].R == u && s[j].G == v)
					repeat = true;
			}
			if (repeat)
				continue;
			//符合条件,渡河
			k++;
			s[k].R = u;
			s[k].G = v;
			choice[k] = i;//记录决策
			break;

		}
		if (i > 5)
		{
			choice[k + 1] = 0;//所有策略都不成功,则需要回退
			k--;
		}
	} while (!(s[k].R == 0 && s[k].G == 0));
}

void display() {
	for (int i = 1; i <= k; i++)
	{
		cout << setw(2) << i << ":choice=" << choice[i]
			<< "{" << d[choice[i]].R << "," << d[choice[i]].G << "}"
			<< "(" << s[i].R << "," << s[i].G << ")" << endl;
	}
}//#include<iostream>
#include<iomanip>
using namespace std;
struct state {
	int R, G;
};//东岸的人数与鬼数
state s[20];//记录渡河时的状态转移过程
int choice[20] = { 0 };//决策记录
int k;//渡河步数

state d[6] = { {0,0},{1,1},{0,1},{2,0},{1,0},{0,2} };

void display();
void transfer_state();
int main()
{
	transfer_state();
	display();
	return 0;
}

void transfer_state() {
	k = 1;
	s[1].R = 3;
	s[1].G = 3;
	do {
		int fx = 1;//渡船方向
		if (k % 2 == 1)
			fx = -1;
		int i;
		for (i = choice[k + 1] + 1; i <= 5; i++)//???
		{
			int u = s[k].R + fx * d[i].R;//按策略i走后东岸人数
			int v = s[k].G + fx * d[i].G;
			if (u > 3 || v > 3 || u < 0 || v < 0)
				continue;//判断是否越界
			if (!(u == 3 || u == 0 || u == v))
				continue;//判断是否安全
			bool repeat = false;//用于判断是否重复
			for (int j = k - 1; j >= 1; j -= 2)//从k-1开始查,仅考虑同方向故j-=2
			{
				if (s[j].R == u && s[j].G == v)
					repeat = true;
			}
			if (repeat)
				continue;
			//符合条件,渡河
			k++;
			s[k].R = u;
			s[k].G = v;
			choice[k] = i;//记录决策
			break;

		}
		if (i > 5)
		{
			choice[k + 1] = 0;//所有策略都不成功,则需要回退
			k--;
		}
	} while (!(s[k].R == 0 && s[k].G == 0));
}

void display() {
	for (int i = 1; i <= k; i++)
	{
		cout << setw(2) << i << ":choice=" << choice[i]
			<< "{" << d[choice[i]].R << "," << d[choice[i]].G << "}"
			<< "(" << s[i].R << "," << s[i].G << ")" << endl;
	}
}

四.求出所有解决方案

解题思路

1.将方向纳入状态坐标
2.通过查找历史记录防止重复

#include<iomanip>
using namespace std;
struct position {
	int x, y;
};

position dxy[] = { {1,0}, {0,1}, {1,1}, {2,0}, {0,2} };
struct state {
	int dir;//方向
	position pos;
};
state start = { -1,{3,3} }, goal = { 1,{0,0} };

state path[100];
int num;
//判断两个状态是否相等
bool IsEq(state st1, state st2)
{
	return (st1.dir == st2.dir)&&(st1.pos.x == st2.pos.x) && (st1.pos.y == st2.pos.y);
}
//是否结束
bool IsDone(state st) {
	return IsEq(st, goal);
}
//判断是否到达终点
bool IsValid(state st, int step)
{
	//是否越界
	if (st.pos.x < 0 || st.pos.x > 3 || st.pos.y < 0 || st.pos.y > 3)
		return false;
	//安全性
	if (st.pos.x != 0 && st.pos.x != 3 && st.pos.x != st.pos.y)
		return false;
	//是否重复
	for (int i = step - 2; i >= 0; i -= 2)
	{
		if (IsEq(st, path[i]))
			return false;
	}
	return true;
}
//更新状态
state GetNewState(state st, int k, int step) {
	state next_st = { -st.dir,
					{st.pos.x + st.dir*dxy[k].x,
					st.pos.y + st.dir*dxy[k].y}
	};
	return next_st;
}

//记录步骤
void LogStep(state st, int step) {
	path[step] = st;
}
//输出某一状态
void OutStep(state st) {
	cout << setw(2) << st.dir << "(" << st.pos.x << "," << st.pos.y << ")";
}
//输出全部
void OutAll(int step) {
	for (int i = 0; i <= step; i++)
		OutStep(path[i]);
	cout << endl;
}
void Jump(state st, int step)
{
	//是否到达终点
	if (IsDone(st)) {
		num++;
		cout << num << ":";
		OutAll(step);
		return;
	}
	for (int k = 0; k < sizeof(dxy)/sizeof(dxy[0]); k++)
	{
		state next_st = GetNewState(st, k, step);
		if (!IsValid(next_st, step + 1))
			continue;
		LogStep(next_st, step + 1);
		Jump(next_st, step + 1);
	}
}

int main()
{
	num = 0;
	LogStep(start, 0);
	Jump(start, 0);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值