题目大意: 在大海中,每个点有一个水流方向(共八个方向,从北开始顺时针方向用0,1,...7表示),船若按照所在位置的水流方向行驶则不消耗能量,其他任何方向消耗1点能量,给一个矩阵记录各点水流方向,再给一个起始点和一个终点,问到达终点消耗的最小能量。
分析:这是一道求最优解的题,可以用bfs解决,既然是搜索,那么状态是什么?状态是坐标以及消耗的能量,哪个是关键的状态?能量,要求消耗能量最小的解,但是我们的搜索顺序是按坐标的顺序,不能保证队列中的状态是按关键状态递增的,也就不能保证求出最优解,怎么办?两种解决办法:1,调整搜索顺序,保证入队顺序是按照关键状态递增的顺序,也就是不能简单的按照坐标搜索,要多加思考一下,我看了一下别人的博客,大概思路就是从当前状态开始,先搜索当前状态能不耗能到达的状态,然后再搜索消耗能量为1到达的状态,这是一个递归的搜索状态,也就能保证搜到所有状态并且状态入队是按照关键状态优先级递增顺序的,也就保证能得到最优解。2,用单调优先队列优化。
利用单调优先队列写的代码大概2000ms,别人说用第一种方法500ms就可以了,神奇,但是这里我以练习单调优先队列bfs为主,所以用的方法2。
用单调优先队列bfs写这道题时,我写到一半,发现这种方法再更新一个状态后,并没有考虑这个状态是否已经入队,而是直接将更新后的新状态加入队列,刚开始我觉得这样会慢,于是我直接用普通队列写了一个类似spfa的方法,交上去后TLE,仔细想了一下,虽然单调优先队列没有把队列中已经存在的状态更新,多加了一个状态,但始终保持一定顺序,而类spfa表面上把状态更新,少加了一个状态,但却因为无序性,多搜了很多状态,所以导致TLE。
下面附ac代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define INF 0xfffffff
struct node{
int x,y,cost;
};
struct cmp{
bool operator()(node& a,node& b)
{
return a.cost>b.cost;
}
};
priority_queue<node,vector<node>,cmp> q;
char g[1005][1005];
int step[1005][1005];
int n,m;
int dir[8][2]={-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1};
void init()
{
int i,j;
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
step[i][j]=INF;
while(!q.empty())
q.pop();
}
bool isbond(int x,int y)
{
if(x<1||x>n||y<1||y>m)
return 1;
return 0;
}
void work(int sx,int sy,int fx,int fy)
{
init();
if(sx==fx&&sy==fy)
{
step[fx][fy]=0;
return ;
}
node s,t;
int i,cur;
step[sx][sy]=0;
s.x=sx,s.y=sy,s.cost=0;
q.push(s);
while(!q.empty())
{
s=q.top();
q.pop();
if(s.x==fx&&s.y==fy)
return;
cur=g[s.x][s.y]-'0';
for(i=0;i<8;++i)
{
t.x=s.x+dir[i][0];
t.y=s.y+dir[i][1];
t.cost=s.cost;
if(i!=cur)
t.cost++;
if(!isbond(t.x,t.y)&&t.cost<step[t.x][t.y])
{
step[t.x][t.y]=t.cost;
q.push(t);
}
}
}
}
int main()
{
int i,j,k,sx,sy,fx,fy;
while(scanf("%d %d",&n,&m)!=EOF)
{
getchar();
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j)
scanf("%c",&g[i][j]);
getchar();
}
scanf("%d",&k);
while(k--)
{
scanf("%d %d %d %d",&sx,&sy,&fx,&fy);
work(sx,sy,fx,fy);
printf("%d\n",step[fx][fy]);
}
}
return 0;
}