⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 knight.cpp

📁 求解骑士游历问题的程序。骑士游历问题是在8X8格的国际象棋棋盘上随意放置一个马
💻 CPP
字号:
// KNIGHT.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream.h>
#include <iomanip.h>

enum BOOLEAN 
{
	FALSE =0,
	TRUE =10
};

const int MAX_WIDTH = 30;
const int MAX_DIR = 8;

class KNIGHT 
{
  public:

	KNIGHT(int width);		// 设置初始状态
	
	void print();			// 打印问题的解
	
	// 根据马的起始位置(start_x, start_y)使用回溯算法求骑士游历问题的一个解
	// (start_x, start_y)必需在所设置的棋盘宽度范围内
	BOOLEAN tourist(int start_x, int start_y);

protected:
	void init_direction();		// 初始化记录所选方向的数组,将每个值置为MAX_DIR
	
	void init_chessboard();		// 初始化记录马在第几步到位每个位置的数组,将每个值置为0
	
	void set_start(int x, int y);	// 设置初始状态,包括初始化方向数组和棋盘数组,并设置马的初始位置
	
	// 在当前位置选择一个方向以推进到下一位置
	// 如果可选择一个方向推进则返回TRUE,否则返回FALSE
	// 将该函数定义为虚函数,以便下面快速游历的类来重定义该函数而产生动态绑定
	virtual BOOLEAN select_direction();
	
	// 从当前位置回溯到上一位置
	// 将该函数定义为虚函数,以便下面快速游历的类来重定义该函数而产生动态绑定
	virtual void backward();
	
	// 从当前位置推进到下一位置
	// 将该函数定义为虚函数,以便下面快速游历的类来重定义该函数而产生动态绑定
	virtual void forward();
	
	// 判断马是否能够走向位置(x, y)。
	// 如果马已经到过该位置,或该位置超出棋盘范围返回FALSE,否则返回TRUE
	BOOLEAN is_legal(int x, int y);
	
	// 判断是否回溯到初始状态
	// 如果步数回到第1步则表示回到初始状态而返回TRUE,否则返回FALSE
	BOOLEAN back_to_start();
	
	// 判断是否游历完所有位置
	// 如果步数等于棋盘格子数则表示游历完所有位置而返回TRUE,否则返回FALSE
	BOOLEAN is_end();
	
	// 下面两个数组用来记住选择某个方向后,推进到下一位置时x方向和y方向的值的变化
	int var_x[MAX_DIR];
	int var_y[MAX_DIR];
	
	int chessboard[MAX_WIDTH][MAX_WIDTH];	// 记录马第几步到达某个位置的棋盘数组
	
	int direction[MAX_WIDTH][MAX_WIDTH];	// 记录马在某个位置是在上一位置选择第几个方向到达的
	int width; 				//棋盘宽度
	int curr_x, curr_y; 			//马的当前位置
	int step; 				//已经游历的步数
	int last_direction; 			//上一位置到当前位置所选的方向
	int total_step;
};

KNIGHT::KNIGHT(int width)
{
	this->width = width;
	init_direction();
	total_step = 0;
}

void KNIGHT::print()
{
	int x, y;
	cout << " +";
	
	for (x = 0; x < width; x = x + 1) 
		cout << "----+";
        cout<<endl;
	
	for (x = 0; x < width; x = x + 1) 
	{
		cout << " |";

		for (y = 0; y < width; y = y + 1) 
			cout << setw(3) << chessboard[x][y] << " |";

		cout << endl;
		cout << " +";

		for (y = 0; y < width; y = y + 1) 
			cout << "----+";
		
		cout << endl;
	}
}

BOOLEAN KNIGHT::is_legal(int x, int y)
{
	if (x < 0 || x >= width) return FALSE;
	if (y < 0 || y >= width) return FALSE;
	if (chessboard[x][y] > 0) return FALSE;
	return TRUE;
}

BOOLEAN KNIGHT::back_to_start()
{
	if (step == 1) return TRUE;
	else return FALSE;
}

BOOLEAN KNIGHT::is_end()
{
	if (step > width * width) return TRUE;
	else return FALSE;
}

void KNIGHT::set_start(int x, int y)
{
	curr_x = x; curr_y = y; step = 1;
	chessboard[x][y] = step; step = step + 1;
	direction[x][y] = MAX_DIR;
	last_direction = MAX_DIR;
}

BOOLEAN KNIGHT::select_direction()
{
	int try_x, try_y;
	
	// last_direction为MAX_DIR表示当前位置是一个新的位置,在新推进到某个位置(curr_x, curr_y)时,
	// direction[curr_x][curr_y]会记录上一位置到(curr_x, curr_y)时所选择的方向,这时
	// last_direction置为MAX_DIR用来标记该位置是新推进的位置。
	if (last_direction == MAX_DIR) 
		last_direction = 0;
	else 
		last_direction = last_direction + 1;
	
	while (last_direction < MAX_DIR) 
	{
		// 看下一步推进到哪个位置是合法的,如果合法则选择该方向。
		try_x = curr_x + var_x[last_direction];
		try_y = curr_y + var_y[last_direction];
		
		if (is_legal(try_x, try_y)) break;
		
		last_direction = last_direction + 1;
	}
	
	if (last_direction == MAX_DIR) 
		return FALSE;
	else 
		return TRUE;
}

void KNIGHT::backward()
{
	step = step - 1;
	chessboard[curr_x][curr_y] = 0;
	
	// 将last_direction置为上一位置到(curr_x, curr_y)所选择的方向
	last_direction = direction[curr_x][curr_y];
	
	// 根据这个方向回溯到上一位置,同时回溯到上一位置之后,在上一位置再试探时应该从
	// last_direction+1的方向开始。参看成员函数select_direction()。
	curr_x = curr_x - var_x[last_direction];
	curr_y = curr_y - var_y[last_direction];
}

void KNIGHT::forward()
{
	// 在推进时last_direction是当前位置所选择的方向
	curr_x = curr_x + var_x[last_direction];
	curr_y = curr_y + var_y[last_direction];
	chessboard[curr_x][curr_y] = step;
	step = step + 1;

	// 这个方向被记录下一位置(这时已经为(curr_x, curr_y))的direction数组中。
	direction[curr_x][curr_y] = last_direction;

	// last_direction的值已经被记录,这时置为MAX_DIR表示当前位置为新推进的位置
	last_direction = MAX_DIR;
}

BOOLEAN KNIGHT::tourist(int start_x, int start_y)
{
	init_chessboard();
	set_start(start_x, start_y);
	do 
	{
		if (select_direction()) forward();
		else backward();
	} while (!back_to_start() && !is_end());
	
	if (back_to_start()) 
		return FALSE;
	else 
		return TRUE;
}

void KNIGHT::init_direction()
{
	var_x[0] = 2; var_y[0] = 1;
	var_x[1] = 1; var_y[1] = 2;
	var_x[2] = -1; var_y[2] = 2;
	var_x[3] = -2; var_y[3] = 1;
	var_x[4] = -2; var_y[4] = -1;
	var_x[5] = -1; var_y[5] = -2;
	var_x[6] = 1; var_y[6] = -2;
	var_x[7] = 2; var_y[7] = -1;
}

void KNIGHT::init_chessboard()
{
	int x, y;
	for (x = 0; x < width; x = x + 1) 
	{
		for (y = 0; y < width; y = y + 1) 
		{
			chessboard[x][y] = 0;
		}
	}
}
// 使用回溯算法快速求解骑士游历问题
class FAST_KNIGHT: public KNIGHT 
{
  public:
	FAST_KNIGHT(int width);
  
  protected:
	void init_cannot();		// 初始化cannot数组
	
	// 在当前位置根据启发式规则选择一个方向以推进到下一位置
	// 如果可选择一个方向推进则返回TRUE,否则返回FALSE
	// 重定义KNIGHT类的select_direction()
	virtual BOOLEAN select_direction();
	
	// 从当前位置回溯到上一位置,注意维持cannot数组
	// 重定义KNIGHT类的backward()
	virtual void backward();
	
	// 从当前位置根据所选择的方向推进到下一位置
	// 重定义KNIGHT类的forward()
	virtual void forward();
	
	BOOLEAN cannot[MAX_WIDTH][MAX_WIDTH][MAX_DIR];	// 用来记住某个位置某个方向是否已经试探过
};

void FAST_KNIGHT::init_cannot()
{
	int x, y, dir;
	for (x = 0; x < width; x = x + 1)
		for (y = 0; y < width; y = y + 1)
			for (dir = 0; dir < width; dir = dir + 1) 
cannot[x][y][dir] = FALSE;
}

FAST_KNIGHT::FAST_KNIGHT(int width): KNIGHT(width)
{
	init_cannot();
}

BOOLEAN FAST_KNIGHT::select_direction()
{
	int try_x, try_y, next_x, next_y;
	int dir_index, next_index;
	int min_dir, count;
	min_dir = MAX_DIR; last_direction = MAX_DIR;
	for (dir_index = 0; dir_index < MAX_DIR; dir_index = dir_index + 1) 
	{
		// 选择一个方向,使得根据该方向推进到下一位置时,在该位置可选的方向最少
		try_x = curr_x + var_x[dir_index];
		try_y = curr_y + var_y[dir_index];
		if (is_legal(try_x, try_y) && !cannot[curr_x][curr_y][dir_index]) 
		{
			// 这个位置作为下一位置是合法,那么计算该位置可选的方向
			count = 0;
			for (next_index = 0; next_index < MAX_DIR; next_index++) 
			{
				next_x = try_x + var_x[next_index];
				next_y = try_y + var_y[next_index];
				if (is_legal(next_x, next_y)) count = count + 1;
			}
			
			if (count < min_dir) 
			{
				// 记录具有最少可选方向的下一位置
				last_direction = dir_index;
				min_dir = count;
			}
		}
	}
	
	if (last_direction == MAX_DIR) 
		return FALSE;
	else 
		return TRUE;
}
void FAST_KNIGHT::backward()
{
	int dir;
	step = step - 1;
	chessboard[curr_x][curr_y] = 0;
	
// 从位置(curr_x, curr_y)回溯,那么下一次再到达该位置时所有方向都需要重新试探
	for (dir = 0; dir < MAX_DIR; dir = dir + 1) 
cannot[curr_x][curr_y][dir] = FALSE;
	last_direction = direction[curr_x][curr_y];
	curr_x = curr_x - var_x[last_direction];
	curr_y = curr_y - var_y[last_direction];
}
void FAST_KNIGHT::forward()
{
	// 该位置的这个方向已经试探过了
	cannot[curr_x][curr_y][last_direction] = TRUE;
	curr_x = curr_x + var_x[last_direction];
	curr_y = curr_y + var_y[last_direction];
	chessboard[curr_x][curr_y] = step;
	step = step + 1;
	direction[curr_x][curr_y] = last_direction;
	last_direction = MAX_DIR;
}

int main()
{
	int width = 8;
	FAST_KNIGHT knight(width);
	int start_x, start_y;
	cout << "\nProgram begin..."<<endl;
	start_x = 4; start_y = 4;
	
	if (knight.tourist(start_x, start_y)) 
	{
		knight.print();
	}
	else 
	{
		cout << "\nHave not found the solution for: ";
		cout << "Start: <" << start_x << ", " << start_y << ">, ";
		cout << "Width: " << width << endl;
	}
	
	return 1;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -