题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1072
这题好不容易调试成功了(因为各种手残。。),结果提交上去是TLE。。于是自己想剪枝方法,但是没想出来。。于是睡了个觉。。醒来还是没想出来。。。而且毫无剪枝思路。。。于是找了找网上的题解,才明白是怎样剪枝的。博客原文如下:
这题不同于其他题的地方就是于虽然也是bfs,但对于走过的路径不能标记,因为可能还要走,注意题目要求:如果可以,可以走任意多遍。这就引发了一个问题,如果不缩减搜索范围,怎么可能走得出来呢?应该说这题好就好在不是根据走过的路径来标记,而是根据前后两次踏上同一位置是bomb离explode的时间长短来标记。简言之,如果第二次踏上一个位置,那么找出路已用的时间肯定是增加了,那为啥还要走上这条路呢?唯一的追求就是bomb离爆炸的时间增大了。所以可以利用这个条件来标记了。每次在入队前检查下爆炸时间是否比上次在同一位置的大,若是,则入队;反之,入队无意义了。
从以上的分析中可以引出另一思路,也就是只要进入位置4,那么bomb就会延时到6分钟,最大的延时时间。换句话说,下次再进入该4位置,也
不会获得更大的延时时间了。所以,只要访问过位置4了,就可以直接标记为0位置,表明下次不可在访问。
我的代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include<algorithm>
using namespace std;
int n, m, vis[10][10], mp[10][10];
int jx[]={0,0,1,-1};
int jy[]={1,-1,0,0};
struct node
{
int x, y, ans, z;
};
void bfs(int a, int b)
{
int i, j;
queue<node>q;
node f1,f2;
f1.x=a;
f1.y=b;
f1.ans=0;
f1.z=6;
vis[a][b]=6;
q.push(f1);
while(!q.empty())
{
f1=q.front();
q.pop();
//printf("%d %d %d %d\n",f1.x,f1.y,f1.ans,f1.z);
if(mp[f1.x][f1.y]==3&&f1.z>0)
{
printf("%d\n",f1.ans);
return ;
}
if(f1.z<=0)
continue ;
for(i=0;i<4;i++)
{
f2.x=f1.x+jx[i];
f2.y=f1.y+jy[i];
f2.z=f1.z-1;
if(f2.z>0&&f2.x>=0&&f2.x<n&&f2.y>=0&&f2.y<m&&(mp[f2.x][f2.y]==1||mp[f2.x][f2.y]==3))
{
f2.ans=f1.ans+1;
if(f2.z>vis[f2.x][f2.y])
{
vis[f2.x][f2.y]=f2.z;
q.push(f2);
}
}
else if(f2.z>0&&f2.x>=0&&f2.x<n&&f2.y>=0&&f2.y<m&&vis[f2.x][f2.y]!=-1&&mp[f2.x][f2.y]==4)
{
f2.z=6;
vis[f2.x][f2.y]=-1;
f2.ans=f1.ans+1;
q.push(f2);
}
}
}
printf("-1\n");
}
int main()
{
int t, i, j;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(vis,0,sizeof(vis));
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
scanf("%d",&mp[i][j]);
}
}
int x=0;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(mp[i][j]==2)
{
x=1;
bfs(i,j);
break;
}
}
if(x)
break;
}
}
return 0;
}