题目背景
在爱与愁的故事第一弹第三章出来前先练练四道基本的回溯/搜索题吧……
题目描述
中国象棋半张棋盘如图 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;
}
这个很安全