您的位置:首页 > 其它

第六届蓝桥杯决赛真题 04 穿越雷区(dfs || bfs)

2017-05-22 23:17 429 查看

穿越雷区

Time Limit : 5000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 18   Accepted Submission(s) : 8

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

X星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废。

某坦克需要从A区到B区去(A,B区本身是安全区,没有正能量或负能量特征),怎样走才能路径最短?

已知的地图是一个方阵,上面用字母标出了A,B区,其它区都标了正号或负号分别表示正负能量辐射区。

例如:

A + - + -

- + - - +

- + + + -

+ - + - +

B + - + -

坦克车只能水平或垂直方向上移动到相邻的区。

Input

第一行输入一个整数T,表示测试数据的组数。

接下来有T组数据,

输入第一行是一个整数n,表示方阵的大小, 4<=n<100

接下来是n行,每行有n个数据,可能是A,B,+,-中的某一个,中间用空格分开。

A,B都只出现一次。

Output

要求每行输出一个整数,表示坦克从A区到B区的最少移动步数。

如果没有方案,则输出-1

Sample Input

1
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -


Sample Output

10

ps:无意中看到了这个题,感觉不错就写了一下,开始用的bfs,后来又用dfs做了一下,个人感觉还是用bfs好一点。

解题思路:请看注释,一定记得吃回车符吃空格符,博主本来直接敲好了代码,就是没吃回车符和空格符卡了好久。

代码1(bfs):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
char mp[105][105];
bool vis[105][105];             //标记数组
bool flag;                  //标记是否找到合适的方案
int dir[4][2]= {{-1,0},{0,-1},{1,0},{0,1}};//搜索的四个方向
int n;
int ans;
int ex,ey;
struct node
{
int x,y,step;
} s,ns;
void bfs(int x,int y)
{
int nx,ny;
queue<node>q;
s.x=x;
s.y=y;
s.step=0;
vis[x][y]=false;
q.push(s);                  //加入第一个点
while(!q.empty())
{
s=q.front();           //弹出第一个点判断
q.pop();               //删除这个点
if(s.x==ex&&s.y==ey)   //搜索到终点就返回
{
ans=s.step;
flag=true;
return ;
}
for(int i=0; i<4; i++)
{
nx=s.x+dir[i][0];
ny=s.y+dir[i][1];
if(nx>=0&&nx<n&&ny>=0&&ny<n&&vis[nx][ny])
{
if(mp[nx][ny]!=mp[s.x][s.y])    //这个节点与上一个节点符号不同才加入队列
{
ns.x=nx;
ns.y=ny;
ns.step=s.step+1;
vis[nx][ny]=false;          //已经访问就标记为false
//printf("push(%d,%d), d=%d\n", ns.x, ns.y, ns.step);
q.push(ns);
}
}
}
}

}
int main()
{
int t;
scanf("%d",&t);
getchar();      //吃回车字符
while(t--)
{
int sx,sy;
flag=false;
memset(vis,true,sizeof(vis));
scanf("%d",&n);
getchar();      //吃回车字符
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
scanf("%c",&mp[i][j]);
if(mp[i][j]=='A')
{
sx=i;
sy=j;
}
if(mp[i][j]=='B')
{
ex=i;
ey=j;
}
getchar();      //吃空格字符或回车符
}
bfs(sx,sy);
if(!flag)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}

/*
3
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
3
A + +
+ + +
+ + B
3
A + -
+ + +
+ + B

答案:
10
-1
4
*/


解题思路:网上查到下面这种方法也可以解决空格符和回车符赋值给字符数组的问题,若使用以下输入语句,那么三个getchar()均可省略。
  * 表示本输入项在读入后不赋值给相应的变量 

在scanf中*被称为:附加格式说明符

scanf("%*c%c",&mp[i][j])
c语言中scanf格式化输入函数详解请点击

然后就是剪枝问题,不然就有可能超时,就是判断一下新路径已有长度是否比原来最短路是否长,是就没必要再找下去了。

代码2(dfs):
#include <iostream>
#include <cstdio>
#include <cstring>
#define inf 0x3f3f3f3f   //无穷大的一般定义
using namespace std;
bool vis[105][105];
char mp[105][105];
int dir[4][2]= {{-1,0},{0,-1},{1,0},{0,1}};
int n,sx,sy,ex,ey;
int ans;
void dfs(int x,int y,int t)
{
int nx,ny;
if(x==ex&&y==ey&&t<ans)  //深搜找的是多组符合题意的而方案,取最优解
{
ans=t;
return;
}
for(int i=0; i<4; i++)
{
nx=x+dir[i][0];
ny=y+dir[i][1];
if(nx>=n||ny>=n||nx<0||ny<0||mp[nx][ny]==mp[x][y]||!vis[nx][ny])
continue;
if(nx>=0&&ny>=0&&nx<n&&ny<n&&mp[nx][ny]!=mp[x][y]&&vis[nx][ny])
{
vis[nx][ny]=false;
if(t<ans)            //剪枝,不然超时
dfs(nx,ny,t+1);
vis[nx][ny]=true;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ans=inf;
memset(vis,true,sizeof(vis));
scanf("%d",&n);
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
scanf("%*c%c",&mp[i][j]);   //*表示本输入项在读入后不赋值给相应的变量
if(mp[i][j]=='A')
{
sx=i;
sy=j;
}
if(mp[i][j]=='B')
{
ex=i;
ey=j;
}
}
dfs(sx,sy,0);
if(ans!=inf)
printf("%d\n",ans);
else
printf("-1\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  BFS dfs ACM 蓝桥杯