http://codeforces.com/contest/877/problem/D
给定一个点,问你从x点到y点,需要的最少时间。
没秒可以走小于等于k步。
暴力的bfs也是没谁了。。怎么可能过。
后来又写了一种方案,枚举每个点往前后左右的最大可移动距离,还是t
(再51nod见过一道类似这种方法的。)
百度之,好多方法。
① 剪枝,相同方向剪,维护局部最小距离剪。
② 把vis变量分为四个方向,然后根据方向剪。
(即对于某一个点。如果相同方向再次使来,肯定不是最短距离了,这个剪枝也不错。)
③ 并查集,并查集大佬qwq
我郁闷了,bfs本来就能在最短的达到,我还维护,根本不用维护,一旦到达那么久肯定是最优解的了qwq
#include <bits/stdc++.h>
using namespace std;
/*这个代码有个细节,我交了几十遍才发现,
那就是,当有另一个方向到达的该点,和曾经该点的最小时间一样大, 那么不要剪掉。。也就是,
那个 vis[x][y]<u.tim+1 ,不能取等号。。。坑死了
*/
const int maxn=1e3+320;
int m,n,k;
char a[maxn][maxn];
struct Node{
int x,y,tim;
Node(){};
Node(int _x,int _y,int _tim){
x=_x;
y=_y;
tim=_tim;
};
};
queue<Node>q;
int x1,y_1,x2,y2;
int vis[maxn][maxn];
int fx[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int main()
{ while(~scanf("%d%d%d",&m,&n,&k)){
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf(" %c",&a[i][j]);
}
}
memset(vis,0x3f,sizeof(vis));
scanf("%d%d%d%d",&x1,&y_1,&x2,&y2);
vis[x1][y_1]=0;
q.push(Node(x1,y_1,0));
int ans=1e9;
while(!q.empty()){
Node u=q.front();
q.pop();
for(int j=0;j<4;j++)
{ for(int i=1;i<=k;i++){
int xx=fx[j][1]*i+u.x;
int yy=fx[j][0]*i+u.y;
if(xx==x2&&yy==y2){
vis[xx][yy]=min(vis[xx][yy],u.tim+1);
continue;
}
if(xx<1||xx>m||yy<1||yy>n)break;
if(vis[xx][yy]<u.tim+1) break;
if(a[xx][yy]=='#') break;
if(vis[xx][yy]>u.tim+1){
vis[xx][yy]=u.tim+1;
q.push(Node(xx,yy,u.tim+1));
}
}
}
}
ans=vis[x2][y2];
if(ans==0x3f3f3f3f) ans=-1;
printf("%d\n",ans);
}
return 0;
}
B 用方向进行剪枝。
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
typedef long long ll;
const int maxn = 1005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m,k;
int sx,sy,ex,ey;
int vis[maxn][maxn];
char mp[maxn][maxn];
int dis[maxn][maxn];
struct node
{
int x,y;
}now,nex;
bool inbound(node a)
{
return a.x>=1&&a.x<=n&&a.y>=1&&a.y<=m;
}
void bfs()
{
queue<node>q;
now.x=sx;
now.y=sy;
q.push(now);
vis[sx][sy]=(1<<4)-1; //起点只经过一次就好
while(q.size())
{
now=q.front();
q.pop();
if(now.x==ex&&now.y==ey) return; //搜到终点
for(int i=0;i<4;i++) //枚举方向
for(int j=1;j<=k;j++) //枚举走的步数
{
nex.x=now.x+dir[i][0]*j;
nex.y=now.y+dir[i][1]*j;
if(!inbound(nex)||mp[nex.x][nex.y]=='#') break; //该位置不合法
if(vis[nex.x][nex.y]&(1<<i)) break; //从这个方向已经走过该位置
int flag=0;
if(!vis[nex.x][nex.y]) flag=1; //搜到没有走过的点
vis[nex.x][nex.y]|=(1<<i); //更新状态
if(flag)
{
dis[nex.x][nex.y]=dis[now.x][now.y]+1;
q.push(nex);
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]+1);
}
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
bfs();
if(vis[ex][ey]) printf("%d\n",dis[ex][ey]);
else puts("-1");
return 0;
}