海龟图&骑士问题&八皇后问题

	总算是把上学期实验欠下的三道选做尝试了一下,小白摸爬滚打,不过欣慰的是画上了一个句号。

海龟图

【题目描述】
龟图logo语言在个人计算机用户中非常流行,该语言形成了龟图的概念。假设有个机器海龟,通过c++程序控制在房子中移动。在两个方向之一打开画笔,即向上或向下。画笔向下时,海龟跟踪移动的形状并留下移动的路径,画笔向上时,海龟自由移动,不写下任何东西。在这个问题中,要模拟海龟的操作和生成计算机化的草图框。
用20 × 20数组floor,初始化为0。从数组中读取命令。跟踪任何时候海龟的当前位置和画笔的向上或向下状态。假设海龟总是从位置(0,0)开始,画笔向上。程序要处理的海龟命令如下:

Figure . Turtle graphics commands.
CommandMeaning
1Pen up
2Pen down
3Turn right
4Turn left
5,10Move forward 10 spaces (or a number other than 10)
6Print the 20-by-20 array
9End of data (sentinel)

假设海龟接近平面中心。下列“程序”绘制和打印12*12的正方形并让画笔向上:

2
5 12
3
5 12
3
5 12
3
5 12
1
6
9

画笔向下并移动海龟时,将数组floor的相应元素设置为1。指定命令6(打印)时,只要数组中有1.就显示星号或选择其他符号,而0则显示空白。编写一个程序,实现这里介绍的龟图功能。编写几个龟图程序,画一些有趣的图形。增加其他命令以增加龟图语言的功能。

【结果输出】
在这里插入图片描述
【我的代码】

#include<iostream>
using namespace std;
void getCommands(int [][2]);
int turnRight(int);
int turnLeft(int);
void movePen(bool,int [20][20],int,int);
void print(int [20][20]);
int main()
{
	int floor[20][20]={0};//floor表示画板大小
	int k=0,dir=0;//0为向右移动,1为向下移动,2为向左移动,3为向上移动
	bool penDown=false;
	int commands[400][2]={0};//假定400为命令输入上限
	getCommands(commands);
	int instruction=commands[k][0];
	while(instruction!=9)
	{
	switch(instruction)
	{
	case 1:penDown=false;break;
	case 2:penDown=true;break;
	case 3:dir=turnRight(dir);break;
	case 4:dir=turnLeft(dir);break;
	case 5:movePen(penDown,floor,dir,commands[k][1]);break;
	case 6:cout << "\nThe drawing is:\n\n";print(floor);break;
	default:break;
	}
	instruction=commands[++k][0];
	}
	system("pause");
	return 0;
}
void getCommands(int commands[][2])
{
	int t,i;
	cout<<"Enter command(9 to end input): ";
	cin>>t;
	for(i=0;t!=9&&i<400;i++)
	{
	commands[i][0]=t;
	if(t==5)cin>>commands[i][1];
	cout<<"Enter command(9 to end input): ";
	cin>>t;
	}
	commands[i][0]=9;
}
int turnRight(int dir)
{
    return ++dir > 3 ? 0 : dir;  //原点为0,右转4次回到原点
}
int turnLeft(int dir)
{
    return --dir < 0 ? 3 : dir;
}
void movePen(bool penDown,int floor[20][20],int dir,int d)
{
static int x=0,y=0;//注:静态变量在每次调用函数时它的值都保存下来
int i;
switch(dir)
{
case 0://向右
	for(i=0;i<d&&(y+i)<20;i++)
	{
	if(penDown)floor[x][y+i]=1;
	else floor[x][y+i]=0;
	}
	y=y+d-1;
	break;
case 1://向下
	for(i=0;i<d&&(x+i)<20;i++)
	{
	if(penDown)floor[x+i][y]=1;
	else floor[x+i][y]=0;
	}
	x=x+d-1;
	break;
case 2://向左
	for(i=0;i<d&&(y-i)>=0;i++)
	{
	if(penDown)floor[x][y-i]=1;
	else floor[x][y-i]=0;
	}
	y=y-d+1;
	break;
case 3://向上
	for(i=0;i<d&&(x-i)>=0;i++)
	{
	if(penDown)floor[x-i][y]=1;
	else floor[x-i][y]=0;
	}
	x=x-d+1;
	break;
}
}
void print(int floor[20][20])
{
	for(int i=0;i<20;i++)
	{
	for (int j=0;j<20;j++)
	{
        if(floor[i][j]==0) cout<<" ";
        else cout<<"*";
	}
	cout<<endl;
	}
}

骑士问题

【问题描述】
在这里插入图片描述(1)
在这里插入图片描述
在这里插入图片描述在这里插入图片描述(2)(骑士旅行:强制方法)
骑士旅行,可以用“访问性试探”能产生许多解法并可以有效地执行。随着计算机的运算能力越来越强,可以用更简单的算法解决这个问题,即采用强制算法。
a) 用随机数产生程序让骑士在棋盘上按L形走法任意移动:程序一步一步走完这个棋盘。骑士能走多远?
b) 通常.上述程序不会走太远。现在修改程序,进行1000次走法。用单下标数组跟踪每次走了几步。程序完成1000次旅行后,以表格形式打印这些信息。最佳结果是付么?
c) 通常,上述程序能得到较好地走法,但无法走遍棋盘。现在删除次数限制,让程序一直运行,直到找出走遍棋盘的方法(注意,可能要好几个小时才能在强大的计算机上完成)。以表格形式打印这些信息,看看要多少次才能找出走遍棋盘的方法,花多少时间。
d) 比较强制算法与前面介绍的访问性试探方法。哪个需要更认真地分析问题,哪个算法更难开发,哪个需要更强大的计算机功能,利用访问性试探能否事先保证找出走遍棋盘的方法,利用强制算法能否事先保证找出走遍棋盘的方法,比较两种方法的利与弊。

(3)(骑士族行:闭合线路)
在骑士旅行问题中,骑士经过64格中的每一格一次,且只经过一次。闭合线路就是最后又回到出发的地方。修改骑士旅行程序,测试闭合线路是否走遍了棋盘。

【结果输出】
在这里插入图片描述
【我的代码】
算法真是一个奇妙的东西,感谢一开始题目的引导让我首先用访问性试探方法解决了骑士问题,其核心在于下一次移动时移到访问性值最小的格子,并且需要注意的是访问性值是随着每一次可尝试访问而减少的,这真的得很细心很细心,马大哈的我就是在这上面百思不得其解,不过好得攻克了。(后来,我又知道了,它的学名叫“贪心算法”)

//访问性试探方法
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<iomanip>
using namespace std;
void printBoard(int [8][8]);
int main()
{
	int board[8][8]={0},
		//表示一个8*8棋盘
		currentRow,currentColum,
		//表示骑士当前位置的行与列
		moveNumber=0,
		//进行moveNumber类型的移动(moveNumber在0到7之间)
		//例:0类型的移动为水平右移两格和垂直上移一格
		testRow,testColum,minAccessNumber,minRow,minColum;
	int horizontal[8]={2,1,-1,-2,-2,-1,1,2};//左移为负数
	int vertical[8]={-1,-2,-2,-1,1,2,2,1};//上移为负数
	int access[8][8]={	2,3,4,4,4,4,3,2,
						3,4,6,6,6,6,4,3,
						4,6,8,8,8,8,6,4,
						4,6,8,8,8,8,6,4,
						4,6,8,8,8,8,6,4,
						4,6,8,8,8,8,6,4,
						3,4,6,6,6,6,4,3,
						2,3,4,4,4,4,3,2,	};
	//棋盘上任一格子上访问性值是该格子能访问的格数
	bool over=false;
	srand(time(0));
	currentRow=rand()%8;
	currentColum=rand()%8;//从棋盘上任一一格开始运行64次旅行
	board[currentRow][currentColum]=++moveNumber;
	while(!over)
	{
	minAccessNumber=9;
	for(int i=0;i<8;i++)
		{
			testRow=currentRow+vertical[i];
			testColum=currentColum+horizontal[i];
		if(testRow>7||testRow<0||testColum>7||testColum<0)continue;
		//确认进行i类型的移动后不会出棋盘
		if(board[testRow][testColum]==0)//确认之前未走到该点
			{
				if(access[testRow][testColum]<minAccessNumber)
				{
				minAccessNumber=access[testRow][testColum];
				minRow=testRow;
				minColum=testColum;
				}//为了移到下次移动时访问性值最小的格子
				access[testRow][testColum]--;
				//这步可知上一步位置不可访问,所以访问性值-1
			}
		}
	if(minAccessNumber==9) over=true;//找不到未走过的点,循环结束
	else {
		currentRow=minRow;
		currentColum=minColum;
		board[currentRow][currentColum]=++moveNumber;}
	}
	cout<<"The tour ended with "<<moveNumber<<" moves."<<endl;
	cout<<"This was a full tour!\n"<<endl;
	printBoard(board);
	system("pause");
	return 0;
}
void printBoard(int board[8][8])
{
	cout<<"   0  1  2  3  4  5  6  7"<<endl;
	for(int i=0;i<8;i++)
	{
	cout<<i;
	for(int j=0;j<8;j++)
		cout<<setw(3)<<board[i][j];
	cout<<endl;
	}
}

接下来,题目引导尝试强制算法,一开始在次数限制的情况下的确无法走遍棋盘,删除次数限制,让程序一直运行,直到找出走遍棋盘的方法(注意,可能要好几个小时才能在强大的计算机上完成)。
之后访问大佬的博客,这种算法在计算人圈中学名是不是就叫“回溯算法”(还是不敢断言),采用穷举的方式,所以运行时间的长就可想而知。但是这其中也包含递归算法(我是那么理解的),所以也不是真正意义上的穷举啦!

//回溯算法
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<iomanip>
using namespace std;
void printSolution(int [8][8]);
bool findSolution(int ,int ,int );
int board[8][8]={0};
int horizontal[8]={2,1,-1,-2,-2,-1,1,2};
int vertical[8]={-1,-2,-2,-1,1,2,2,1};
int main()
{
	int currentRow,currentColum,moveNumber=1;
	srand(time(0));
	currentRow=rand()%8;
	currentColum=rand()%8;//从棋盘上任一一格开始运行64次旅行
	if(findSolution(currentRow,currentColum,moveNumber))
	{
		cout<<"The tour ended with 64 moves."<<endl;
		cout<<"This was a full tour!\n"<<endl;
		printSolution(board);
	}
	else cout<<"Solution does not exist"<<endl;
	system("pause");
	return 0;
}
bool findSolution(int currentRow,int currentColum,int moveNumber)
{
	board[currentRow][currentColum]=moveNumber;
	if(moveNumber==64) return true;
	int testRow,testColum;
	for(int i=0;i<8;i++)
	{
		testRow=currentRow+vertical[i];
		testColum=currentColum+horizontal[i];
		if(testRow>=0&&testRow<8&&testColum>=0&&testColum<8&&board[testRow][testColum]==0)
		{
			if(findSolution(testRow,testColum,moveNumber+1))return true;
		}
	}
	board[currentRow][currentColum]=0;
	//回溯操作,表示这一步往后没有解决方案,归零,从上一步开始另外寻找解决方案
	return false;
}
void printSolution(int board[8][8])
{
	cout<<"   0  1  2  3  4  5  6  7"<<endl;
	for(int i=0;i<8;i++)
	{
	cout<<i;
	for(int j=0;j<8;j++)
		cout<<setw(3)<<board[i][j];
	cout<<endl;
	}
}

运行时间那就看随机数开始的位置了,一切都是随缘,10+min,1+h甚至可能更久(哇!这是我第一次遇到!)

八皇后问题

【问题描述】
(八皇后) 国际象棋中的另一个难题是八皇后问题。
简单地说,空棋盘上能否放八个皇后,使一个皇后不会“攻击”另一个皇后,即不会有两个皇后在同一行、同一列或同—对角线上。

(1) 用访问性试探法解决八皇后问题
(提示:棋盘上的每一格可以指定一个值,表示一个皇后可以“删除”多少个格子,四个角落指定数值22,如下图);在64个格子中放上这些“删除”数之后,问题就变成将下一个皇后放在删除数最少的格子中。
在这里插入图片描述
(2) (八皇后:强制算法)
这个练习要用几个强制算法解决八皇后问题。

a)随机强制算法解决八皇后问题。
b)穷举法,即测试八个皇后在棋盘上的各种组合。
c) 说明穷举法为什么不适用于骑士旅行间题?
d) 比较随机强制算法与穷举法。

(3)递归方法解决八皇后问题

【结果输出】
首先通过数学方法知,八皇后问题在不考虑旋转与翻转等价时,共有92种不同的解。
这是回溯算法得到的第一个解(如下)。
在这里插入图片描述
当然我们还可以不停止循环,用变量sum做一点改变就能得到92种答案。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
剩下72个实在不想继续截下去了,原谅我的懒惰,当它72变变没了吧,嘻嘻。

【我的代码】
经过骑士问题后,我是真的喜欢访问性试探法,因为我是懒癌患者。但是八皇后问题我并没有首当其冲用这个解决出来。可见,方法的好坏还是得看题目而定,嘤嘤嘤。
反而,我采用了回溯算法解决出来,因为它很好设计,理解上也较为容易。我设计了程序:找到第一解,就结束循环,所以其运行结果是唯一的。

//回溯算法
#include<iostream>
#include<iomanip>
using namespace std;
int b[9]={0},
	//b[1..8]控制同一列只有一个皇后
	d[15]={0},c[17]={0};
	//d[-7..7]、c[1..16]控制同一对角线上只能有一个皇后
int a[9];//a[i]=j表示第i个皇后放在第i行第j列(八个皇后一定是不同行,所以一行一行的放)
void search(int);
void print();
int main()
{
	search(1);//从第1个皇后开始放置
	system("pause");
	return 0;
}
void search(int i)
{
for(int j=1;j<=8;j++)
	if((!b[j])&&(!c[i+j])&&(!d[i-j+7]))//寻找放置皇后的位置
									//+7是由于数组不能为负
	{
	a[i]=j;//摆放皇后
	b[j]=1;//表示占领第j列
	c[i+j]=1;//在/斜线上,行列值之和相同
	d[i-j+7]=1;//
	if(i==8){print();break;}//8个皇后都放置好,输出
	else search(i+1);
	b[j]=0;//递归返回即为回溯一步,当前皇后退出
	c[i+j]=0;
	d[i-j+7]=0;
	}
}
void print()
{
	cout<<"   1  2  3  4  5  6  7  8"<<endl;
	for(int i=1;i<=8;i++)
	{
	cout<<i;
	for(int j=1;j<=8;j++)
		if(a[i]==j)cout<<setw(3)<<"Q";
		else cout<<setw(3)<<"*";
	cout<<endl;
	}
}

一点小改变(不停止循环直到输出所有结果):

#include<iostream>
#include<iomanip>
using namespace std;
int b[9]={0},
	//b[1..8]控制同一列只有一个皇后
	d[15]={0},c[17]={0};
	//d[-7..7]、c[1..16]控制同一对角线上只能有一个皇后
int sum=0,a[9];//a[i]=j表示第i个皇后放在第i行第j列(八个皇后一定是不同行,所以一行一行的放)
void search(int);
void print();
int main()
{
	search(1);//从第1个皇后开始放置
	system("pause");
	return 0;
}
void search(int i)
{
for(int j=1;j<=8;j++)
	if((!b[j])&&(!c[i+j])&&(!d[i-j+7]))//寻找放置皇后的位置
									//+7是由于数组不能为负
	{
	a[i]=j;//摆放皇后
	b[j]=1;//表示占领第j列
	c[i+j]=1;//在/斜线上,行列值之和相同
	d[i-j+7]=1;//在\斜线上,行列值之差相同
	if(i==8){print();}//8个皇后都放置好,输出
	else search(i+1);
	b[j]=0;//递归返回即为回溯一步,当前皇后退出
	c[i+j]=0;
	d[i-j+7]=0;
	}
}
void print()
{
	sum++;//方案数+1
	cout<<"case "<<sum<<":"<<endl;
	cout<<"   1  2  3  4  5  6  7  8"<<endl;//输出一种方案
	for(int i=1;i<=8;i++)
	{
	cout<<i;
	for(int j=1;j<=8;j++)
		if(a[i]==j)cout<<setw(3)<<"Q";
		else cout<<setw(3)<<"*";
	cout<<endl;
	}
}

我真的试图用贪心算法(访问性试探法)设计代码解决,但还是没运行出来,今天情人节虽然不过,还是希望舒心些,我暂且放一放。
之后是我用很好理解的盲目枚举法(穷举法),发现与我的第二个代码输出一致,我算是放心了。

//盲目枚举法
#include<iostream>
#include<iomanip>
using namespace std;
void print();
int check(int[],int);
int sum=0,a[9];//a[i]=j表示第i个皇后放在第i行第j列
int main()
{
for(a[1]=1;a[1]<=8;a[1]++)
	for(a[2]=1;a[2]<=8;a[2]++)
		for(a[3]=1;a[3]<=8;a[3]++)
			for(a[4]=1;a[4]<=8;a[4]++)
				for(a[5]=1;a[5]<=8;a[5]++)
					for(a[6]=1;a[6]<=8;a[6]++)
						for(a[7]=1;a[7]<=8;a[7]++)
							for(a[8]=1;a[8]<=8;a[8]++)
							{	if(check(a,8)==0) continue;
							else print();}
system("pause");
return 0;
}
void print()
{
	sum++;//方案数+1
	cout<<"case "<<sum<<":"<<endl;
	cout<<"   1  2  3  4  5  6  7  8"<<endl;//输出一种方案
	for(int i=1;i<=8;i++)
	{
	cout<<i;
	for(int j=1;j<=8;j++)
		if(a[i]==j)cout<<setw(3)<<"Q";
		else cout<<setw(3)<<"*";
	cout<<endl;
	}
}
int check(int a[],int n)
{
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i-1;j++)
			if(a[i]==a[j]||abs(a[i]-a[j])==abs(i-j))return 0;
	return 1;
}
遗留问题:N皇后问题的贪心算法???
		 N皇后问题的随机强制算法???
		 骑士问题题目中引导的强制算法==回溯算法???
		 N皇后问题我采用的回溯解法不完全等同于
		 该类问题的递归解法吧?还有多少解法呢???
wish continue!
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值