骑士 概率 记忆化搜索

骑士(利用step by step解题)

题目描述

给出一个空的国际象棋棋盘(8x8方格),棋子骑士放在其中一个方格上。国际象棋骑士走一步的8个方向如下图所示:
骑士
一开始骑士所在的格子是第x行第y列。如果骑士移动n次,每次随机均匀地挑选八个方向中的一个(可能是使骑士离开棋盘的方向),求在n次跳跃后它仍然在棋盘上的概率。一旦骑士离开了棋盘,它就无法再次进入。

输入格式

多组测试数据。
第一行,一个整数G,表示有G组测试数据。 1 <= G <= 10
每组测试数据格式:
第一行,三个整数: x, y,n。 1 <= x,y <=8。 1 <=n <=100。 一开始骑士所在的格子是第x行第y列。左下角格子是(1,1)

输出格式

共G行,共G行,每行一个实数,误差不超过0.0001。

输入样例
10
1 1 2
4 4 1
2 3 10
4 3 50
3 7 1
7 1 1
8 6 2
5 8 3
7 1 4
1 3 5
输出样例
0.1875
1.0
0.0522148497402668
8.356427906809618E-7
0.75
0.375
0.359375
0.28515625
0.1533203125
0.149078369140625

解题思路

题目大意:给出骑士最开始的位置,求骑士移动n次后,仍停留在棋盘上的概率

本题是不是很像一道dfs或bfs呢!经典的题目设定我们就用经典的解法去做,本题我们用dfs深度优先搜索

但是我们要怎样深搜去求概率呢?由题可知,对于任意一步,我们都有8种跳法,经过n次跳跃之后,我们可以得到以下的搜索树:

搜索树
这些状态都可以用 d f s ( x , y , s t e p ) dfs(x,y,step) dfs(x,y,step)来表示骑士当前在(x,y),还要走step步仍留在棋盘的概率

d f s ( x , y , s t e p ) = d f s ( x + 1 , y + 2 , s t e p + 1 ) × 1 8 + d f s ( x + 2 , y + 1 , s t e p + 1 ) × 1 8 + d f s ( x + 2 , y − 1 , s t e p + 1 ) × 1 8 ⋯ dfs(x,y,step)=dfs(x+1,y+2,step+1)\times\frac{1}{8}+dfs(x+2,y+1,step+1)\times\frac{1}{8}+dfs(x+2,y-1,step+1)\times\frac{1}{8}\cdots dfs(x,y,step)=dfs(x+1,y+2,step+1)×81+dfs(x+2,y+1,step+1)×81+dfs(x+2,y1,step+1)×81

当(x,y)不在棋盘上时,这一步返回一个0,就不会计入答案。

当step=n时,且(x,y)在棋盘上,返回一个1,计入答案。

但是本题的数据范围很大,且重复的子问题很多,所以我们要用到记忆化搜索

r e c [ x ] [ y ] [ s t e p ] rec[x][y][step] rec[x][y][step]记录骑士当前在(x,y),还要走step步仍留在棋盘的概率

下一次如果碰巧遍历到这个点就可以直接返回值了。

总感觉没什么好说的,看代码注释。


代码

#include<bits/stdc++.h>

using namespace std;
int G,x,y,n;
int dx[10]={0,1,2,2,1,-1,-2,-2,-1};
int dy[10]={0,2,1,-1,-2,-2,-1,1,2};
double rec[10][10][110];
bool vis[10][10][110];

bool onboard(int x,int y)//判断是否在棋盘上 
{
	if(x<=8&&x>=1&&y<=8&&y>=1) return 1;
	else return 0;
}

double dfs(int x,int y,int step)
{
	if(onboard(x,y)==0) return 0.0;//如果不在棋盘上,那么就出局了
	if(step==0) return 1.0;//如果这n步全部跳完了,那么成功
	if(vis[x][y][step]==1) return rec[x][y][step];//记忆化
	for(int i=1;i<=8;i++)//走八个方向
		rec[x][y][step]+=(1.00/8.00)*dfs(x+dx[i],y+dy[i],step-1);//走到这一步的概率是八分之一
	vis[x][y][step]=1;
	return rec[x][y][step];
}

int main()
{
	freopen("2799.in","r",stdin);
	freopen("2799.out","w",stdout);
	cin>>G;
	for(int gr=1;gr<=G;gr++)
	{
		cin>>x>>y>>n;
		memset(vis,0,sizeof(vis));
		memset(rec,0.0,sizeof(rec));
		cout<<fixed<<setprecision(6)<<dfs(x,y,n)<<endl;
	}
	return 0;
}
问题描述:将马随机放在国际象棋的 8X8 棋盘中的某个方格中,马按走棋规则进行移动。要求每个方格上只进入一,走遍棋盘上全部 64 个方格。编制递归程序,求出马的行走路线 ,并按求出的行走路线,将数字 1,2,…,64 依填入 8X8 的方阵输出之。测试数据:由读者指定可自行指定一个马的初始位置。实现提示:每次在多个可走位置中选择一个进行试探,其余未曾试探过的可走位置必须用适当结构妥善管理,以备试探失败时的“回溯”悔棋使用。并探讨每次选择位置的“最佳策略”,以减少回溯的数。 背景介绍: 国际象棋为许多令人着迷的娱乐提供了固定的框架,而这些框架常独立于游戏本身。其中的许多框架都基于骑士奇异的L型移动规则。一个经典的例子是骑士漫游问题。从十八世纪初开始,这个问题就引起了数学家和解密爱好者的注意。简单地说,这个问题要求从棋盘上任一个方格开始按规则移动骑士,使之成功的游历国际象棋棋盘的64个方格,且每个方格都接触且仅接触一。 可以用一种简便的方法表示问题的一个解,即将数字1,……,64按骑士到达的顺序依放入棋盘的方格中。 一种非常巧妙的解决骑士漫游地方法由J.C.Warnsdorff于1823年给出。他给出的规则是:骑士总是移向那些具有最少出口数且尚未到达的方格之一。其中出口数是指通向尚未到达方格的出口数量。在进一步的阅读之前,你可以尝试利用Warnsdorff规则手工构造出该问题的一个解。 实习任务: 编写一个程序来获得马踏棋盘即骑士漫游问题的一个解。 您的程序需要达到下面的要求: 棋盘的规模是8*8; 对于任意给定的初始化位置进行试验,得到漫游问题的解; 对每次实验,按照棋盘矩阵的方式,打印每个格被行径的顺序编号。 技术提示: 解决这类问题的关键是考虑数据在计算机中的存储表示。可能最自然的表示方法就是把棋盘存储在一个8*8的二维数组board中。以(x,y)为起点时骑士可能进行的八种移动。一般来说,位于(x,y)的骑士可能移动到以下方格之一:(x-2,y+1)、(x-1,y+2)、(x+1,y+2)、(x+2,y+1)、(x+2,y-1)、(x+1,y-2)、(x-1,y-2)、(x-2,y-1)。但请注意,如果(x,y)的位置离某一条边较近,有些可能的移动就会把骑士移到棋盘之外,而这当然是不允许的。骑士的八种可能的移动可以用一个数组MoveOffset方便地表示出来: MoveOffset[0]=(-2,1) MoveOffset[1]=(-1,2) MoveOffset[2]=(1,2) MoveOffset[3]=(2,1) MoveOffset[4]=(2,-1) MoveOffset[5]=(1,-2) MoveOffset[6]=(-1,-2) MoveOffset[7]=(-2,-1) 于是,位于(x,y)的骑士可以移动到(x+MoveOffset[k].x, y+MoveOffset[k].y),其中k是0到7之间的某个整数值,并且新方格必须仍位于棋盘上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值