Olya and Energy Drinks
题目链接:http://codeforces.com/problemset/problem/877/D
题目大意:给你一张n*m的地图,人物Olya一秒可以走1~k步,问从起点到终点的最短时间
(1
≤
\leq
≤ n,m,k
≤
\leq
≤ 1000)
Input
3 4 4
....
###.
....
1 1 3 1
Output
3
Note
In the first sample Olya should run 3 meters to the right in the first second, 2 meters down in the second second and 3 meters to the left in the third second.
用bfs是肯定的,bfs能到达的所有的地方,在普通的模板bfs中加一个循环表示步数就行了,但是,如何不重复搜是个问题
普通剪枝: 用二维vs数组标记已经访问过的点,已经访问过的点就不用再次访问了(因为这个题只是简单地计算步数时间,根据bfs宽搜的特点可以保证-----再次访问这个点时用的时间不会比第一次更短,所以是否也不用定义一个数组,判断如果到达该点的时间大于最短时间,就不用入队?但是很多博客都记录了最短时间,而且好像也可以不用优先队列,疑惑。。 )
神操作: 我们仍需要用vs数组来标记,但是之前的是标记该位置是否经过。现在需要保存该位置是否已遍历过这四个方向的情况,如果在这个方向已经遍历过了就直接break,这里我们用二进制来表示这个状态。
解释:假设现在只是用第一种方法标记,如下,假设现在从(1,1)点开始向右遍历,k=7,它遍历到第7个点时,1~7个点都已入队,且被标记了,然后继续看,它遍历到点2的时候,它又会向右遍历完所有的点(虽然右边的点经判断后不会入队),这样就会多了很多不必要的操作
1 2 3 4 5 6 7
1. . . . . . .
代码:
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
char mp[2000][2000];
int vs[2000][2000],n,m,k,x,y,sx,sy;
int dx[]= {0,0,-1,1};
int dy[]= {1,-1,0,0};
struct node
{
int x,y,step;
} now,net;
int check(int x,int y)
{
if(x<1||x>n||y<1||y>m)
return 0;
if(mp[x][y]=='#')
return 0;
return 1;
}
int bfs()
{
queue<node>qu;
now.x=x;
now.y=y;
now.step=0;
vs[x][y]=1;
qu.push(now);
while(!qu.empty())
{
now=qu.front();
qu.pop();
if(now.x==sx&&now.y==sy)
return now.step;
for(int i=0; i<4; i++)
{
for(int j=1; j<=k; j++)
{
net.x=now.x+j*dx[i];
net.y=now.y+j*dy[i];
net.step=now.step+1;
if(!check(net.x,net.y))break;//该点越界或为‘#’
if(vs[net.x][net.y]&(1<<i))break;//主角,,该反方向上已经遍历过
if(!vs[net.x][net.y])
{
if(net.x==sx&&net.y==sy)
return net.step;
qu.push(net);
}
vs[net.x][net.y]|=(1<<i);
}
}
}
return -1;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(vs,0,sizeof(vs));
for(int i=1; i<=n; i++)
scanf("%s",mp[i]+1);
scanf("%d%d%d%d",&x,&y,&sx,&sy);
int ans=bfs();
printf("%d\n",ans);
}
}
后面用神操作a了之后,发现就用第一种方法也能过,不过有两个要注意的地方
1、Olya and Energy Drinks【Codeforces 877D】【BFS+思维+剪枝】
2、上面的问题解决了之后,在进入队列之前加了一个是否到达终点返回的条件就过了,不知道是不是巧合,不过以后写bfs都可以加上吧
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
char mp[2000][2000];
int vs[2000][2000],n,m,k,x,y,sx,sy;
int dx[]= {0,0,-1,1};
int dy[]= {1,-1,0,0};
struct node
{
int x,y,step;
} now,net;
int check(int x,int y)
{
if(x<1||x>n||y<1||y>m)
return 0;
if(mp[x][y]=='#')
return 0;
return 1;
}
int bfs()
{
queue<node>qu;
now.x=x;
now.y=y;
now.step=0;
vs[x][y]=1;
qu.push(now);
while(!qu.empty())
{
now=qu.front();
qu.pop();
if(now.x==sx&&now.y==sy)
return now.step;
for(int i=0; i<4; i++)
{
for(int j=1; j<=k; j++)
{
net.x=now.x+j*dx[i];
net.y=now.y+j*dy[i];
net.step=now.step+1;
if(vs[net.x][net.y])
continue;
if(check(net.x,net.y))
{
if(net.x==sx&&net.y==sy)
return net.step;
qu.push(net);
vs[net.x][net.y]=1;
}
else
break;
}
}
}
return -1;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(vs,0,sizeof(vs));
for(int i=1; i<=n; i++)
scanf("%s",mp[i]+1);
scanf("%d%d%d%d",&x,&y,&sx,&sy);
int ans=bfs();
printf("%d\n",ans);
}
}
还有一道优先队列优化bfs的题:hdu 1242:Rescue
(因为广搜的过程中不能保证时间少的都在队头,所以应该放入优先队列,同时设置结构体时间少的为优先级高的)
代码:
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
int n,m,vs[300][300],x,y,sx,sy;
int dx[]= {1,-1,0,0};
int dy[]= {0,0,-1,1};
char mp[300][300];
struct node
{
int x,y,step;
friend bool operator<(node a,node b)
{
return a.step>b.step;
}
} now,net;
int check(int x,int y)
{
if(x<1||x>n||y<1||y>m)
return 0;
if(vs[x][y])
return 0;
if(mp[x][y]=='#')
return 0;
return 1;
}
int bfs()
{
priority_queue<node>qu;
now.x=x;
now.y=y;
now.step=0;
vs[x][y]=1;
qu.push(now);
while(!qu.empty())
{
now=qu.top();
qu.pop();
if(now.x==sx&&now.y==sy)
return now.step;
for(int i=0; i<4; i++)
{
net.x=now.x+dx[i];
net.y=now.y+dy[i];
if(check(net.x,net.y))
{
net.step=now.step+1;
if(mp[net.x][net.y]=='x')
net.step++;
qu.push(net);
vs[net.x][net.y]=1;
}
}
}
return -1;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(vs,0,sizeof(vs));
for(int i=1; i<=n; i++)
scanf("%s",mp[i]+1);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(mp[i][j]=='a')
sx=i,sy=j;
else if(mp[i][j]=='r')
x=i,y=j;
int ans=bfs();
if(ans==-1)
printf("Poor ANGEL has to stay in the prison all his life.\n");
else
printf("%d\n",ans);
}
}