[BZOJ1644][Usaco2007 Oct]Obstacle Course 障碍训练课(spfa)

题目描述

传送门

题解

看题之后一上手写了个dfs,结果发现tle了?
分析一下dfs的复杂度:如果不考虑最优化剪枝的话,是搜到终点再返回的。那么在最坏情况下,每个点的访问次数是指数级别的。
这不T就鬼了啊
改成spfa之后就A了。由于每一个点从四个方向走过来时的方案有可能是不同的,所以f(i,j,k)表示到(i,j)这个点,是从方向k走过来的最优值。也就相当于把一个点拆成了4个来做。
spfa的复杂度是 O(kE) 的,k一般为2或3,所以非常稳

代码

dfs(tle)

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 105
#define inf 2000000000

int n,sx,sy,tx,ty,ans;
char a[N][N],s[N];
int f[N][N][5];
int dx[5]={0,0,0,1,-1};int dy[5]={0,1,-1,0,0};

void dfs(int x,int y,int dep,int dir)
{
    if (dep>=f[x][y][dir]||dep>=ans) return;
    f[x][y][dir]=dep;
    if (x==tx&&y==ty)
    {
        ans=min(ans,dep);
        return;
    }
    for (int i=1;i<=4;++i)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if (nx<1||nx>n||ny<1||ny>n||a[nx][ny]=='x') continue;
        if (!dir||dir==i) dfs(nx,ny,dep,i);
        else dfs(nx,ny,dep+1,i);
    }
}
int main()
{
    scanf("%d\n",&n);
    for (int i=1;i<=n;++i)
    {
        gets(s);
        for (int j=1;j<=n;++j)
        {
            a[i][j]=s[j-1];
            if (s[j-1]=='A') sx=i,sy=j;
            if (s[j-1]=='B') tx=i,ty=j;
        }
    }
    ans=inf;
    memset(f,127,sizeof(f));
    f[sx][sy][1]=f[sx][sy][2]=f[sx][sy][3]=f[sx][sy][4]=0;
    dfs(sx,sy,0,0);
    printf("%d\n",ans);
}

spfa

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define N 105

int n,sx,sy,tx,ty,ans;
char a[N][N],s[N];
int f[N][N][4];
bool vis[N][N][4];
int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};
struct hp{int x,y,dir;};
queue <hp> q;

int Min(int a,int b,int c,int d)
{
    if (a>b) a=b;
    if (a>c) a=c;
    if (a>d) a=d;
    return a;
}
int main()
{
    scanf("%d\n",&n);
    for (int i=1;i<=n;++i)
    {
        gets(s);
        for (int j=1;j<=n;++j)
        {
            a[i][j]=s[j-1];
            if (s[j-1]=='A') sx=i,sy=j;
            if (s[j-1]=='B') tx=i,ty=j;
        }
    }
    memset(f,127,sizeof(f));
    f[sx][sy][0]=f[sx][sy][1]=f[sx][sy][2]=f[sx][sy][3]=0;
    vis[sx][sy][0]=vis[sx][sy][1]=vis[sx][sy][2]=vis[sx][sy][3]=true;
    q.push((hp){sx,sy,0});q.push((hp){sx,sy,1});q.push((hp){sx,sy,2});q.push((hp){sx,sy,3});
    while (!q.empty())
    {
        hp now=q.front();q.pop();
        vis[now.x][now.y][now.dir]=false;
        for (int i=0;i<4;++i)
        {
            int x=now.x+dx[i],y=now.y+dy[i];
            if (x<1||x>n||y<1||y>n||a[x][y]=='x') continue;
            int len;
            if (i==now.dir) len=0;
            else len=1;
            if (f[x][y][i]>f[now.x][now.y][now.dir]+len)
            {
                f[x][y][i]=f[now.x][now.y][now.dir]+len;
                if (!vis[x][y][i]) vis[x][y][i]=true,q.push((hp){x,y,i});
            }
        }
    }
    ans=Min(f[tx][ty][0],f[tx][ty][1],f[tx][ty][2],f[tx][ty][3]);
    printf("%d\n",ans);
}


总结

①永远不要忘记分析时间复杂度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值