Codeforces Round #442 (Div. 2)-广搜&剪枝&技巧&好题-D. Olya and Energy Drinks

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;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值