P1644 跳马问题 --- bfs+dfs

题目背景

在爱与愁的故事第一弹第三章出来前先练练四道基本的回溯/搜索题吧……

题目描述

中国象棋半张棋盘如图 1 所示。马自左下角 (0,0) 向右上角 (m,n) 跳。规定只能往右跳,不准往左跳。比如图 1 中所示为一种跳行路线,并将路径总数打印出来。

输入格式

只有一行:两个数 n,m。

输出格式

只有一个数:总方案数 total。

输入输出样例

输入 #1

4 8

输出 #1

37

说明/提示

对于 100% 的数据:n,m≤18

 

 

 

 


题解

做完此题倒是令我好生得意。

我写了三种代码,一个bfs,一个stl版bfs,还有一个dfs。

 

手动bfs

这里确实就是bfs的套路。

这里拿《算法图解》中的案例来说明。

 

原书中使用了“在朋友关系网中找芒果经销商”的案例问题。这个更典型一点。

这里我自己画个图。

 

 

解释流程:

开始,队列中仅我一人。我弹出自己,判断自己不是经销商,然后就把我自己的一级朋友(小明、小李、小王)尾端加入队列。然后标记自己已经被检查过。

自己被弹出后,现在队列有了三个新元素。

弹出小明,判断他是不是,他若是,便结束了,我已经找到了。他不是,便把他的一级朋友(小红、小兰)再加入队列(我已被标记,故不再重复加入)。然后标记好小明不是。

如此循环往复,直至找到或者全部查完(队列为空)。

 

那么回到本题,便也是近似的思路。只是有微小不同。

弹出队列首端的一点,判断它是不是到了(m,n)。若是,便计数器+1。若不是,便把它的下一步的4种点判断合理后加入队列。

一直检查,直到队列为空。

 

另外在写代码的时候,虽然思路看起来是弹出+判断+加入队列。但实际上,对队首的元素的判断和入队操作应该放在前面,然后再弹出队首元素。

 

ac代码

#include<bits/stdc++.h>
using namespace std;

const int mx[10]={0,1,2,2,1},my[10]={0,2,1,-1,-2};  //4种移动指标 

struct node{	                //点集 
	int x,y;
}p[200000000];

int head=1,tail=1,ans=0;

int main()
{
	int n,m;
	cin>>n>>m;             //最后点(m,n),注意顺序
	
	p[head].x=0,p[head].y=0;    //初始化队列

	while(head<=tail)           //队列中尚有元素
	{
		if(p[head].x==m && p[head].y==n)        //判断是否到达
		{
			ans++;
		}
		 
		for(int i=1;i<=4;i++)            //下一步的4种考量
		{
			if(p[head].x+mx[i]<=m&&p[head].y+my[i]>=0&&p[head].y+my[i]<=n)  //合理性判断
			{
				p[++tail].x = p[head].x+mx[i];
				p[tail].y = p[head].y+my[i];
			}
		}
		head++;
	} 
	cout<<ans<<endl;
	return 0;
}

 

细节:

点集数组绝对要开得够大才行。

开的小了,便

自己粗略的算了算。

按全局(静态)区大小为2G算,G和M算1000的换算(实际为1024)。

int为4type。

可开int数组为2*10^9/4 = 5*10^8。

这里结构体一个算两个int,再除二,便可开 250000000 大小的数组。这里我开到了200000000,便够了。

 

补充说明:

计算所占空间字节大小(主要是指数组),一来可以手算,用数组大小*单一变量所占字节大小。

二来可以使用sizeof()函数,直接输出sizeof(a),就是输出了a数组所占字节数。

 

 

 

 

STL版bfs

昨天稍微了解了一下stl的queue,故来试试手。

 

ac代码

#include<bits/stdc++.h>
using namespace std;

const int mx[10]={0,1,2,2,1},my[10]={0,2,1,-1,-2};  //4种移动指标 

struct node{	//点集 
	int x,y;
}item;

queue<struct node>q;
int ans=0;

int main()
{
	int n,m;
	cin>>n>>m;       //最后点(m,n)
	
	item.x=0,item.y=0;
	q.push(item);
	
	while(q.size())
	{
		if(q.front().x==m && q.front().y==n)
		{
			ans++;
		}
		 
		for(int i=1;i<=4;i++)
		{
			item.x = q.front().x+mx[i];
			item.y = q.front().y+my[i];
			if(item.x<=m&&item.y>=0&&item.y<=n)
			{
				q.push(item);
			}
		}
		q.pop();
	} 
	cout<<ans<<endl;
	return 0;
}

基本完全一致。

 

细节:

对于结构队列,定义时在type处要改为结构体类型。

queue<struct node>q;

push元素时,传入的也是结构元素。

struct node{	
	int x,y;
}item;

queue<struct node>q;

//入队操作
item.x=0,item.y=0;
q.push(item);

 

 

其实没有ac  0.0

在我的电脑上运行大的案例是可以出结果的(不会RE),但是超时了,也许STL的queue效率不高吧。

我没查到相关资料,只能留待以后解决了。

 

 

 

dfs写法

 

其实本题也可以用dfs,刚好最近dfs写得多,就来练练手。

ac代码

#include<bits/stdc++.h>
using namespace std;

const int mx[10]={0,1,2,2,1},my[10]={0,2,1,-1,-2};  //4种移动指标 
int ans=0;
int n,m;


void dfs(int x,int y)
{
	if(x==m&&y==n)
	{
		ans++;
		return;
	}
	for(int i=1;i<=4;i++)
	{
		if(x+mx[i]<=m&&y+my[i]>=0&&y+my[i]<=n)
		{
			dfs(x+mx[i],y+my[i]);
		}
	}
}

int main()
{
	int x=0,y=0;
	cin>>n>>m;
	dfs(0,0);
	cout<<ans<<endl;
	return 0;
}

这个很安全

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值