noip2013 华容道 bfs+建图

解决NOIP2013华容道问题时,单纯使用BFS在多次询问下会导致超时(TLE)。通过观察,问题可转换为图上的查询。通过预处理dis[x][y][k][h],表示位置(x, y)在方向k上有空格,到达方向h的最短步数,进行n^2×4×4次BFS。之后利用状态转移,采用SPFA算法进行状态更新。在每次查询时,从起点到终点计算答案,注意处理起点与终点相同的情形。" 51245569,5630888,Java实现求最长增长子序列及子序列值,"['Java编程', '算法实现', '动态规划']
摘要由CSDN通过智能技术生成

这道题有多次询问,如果每次询问bfs做的话,会TLE。发现询问是在一张图上,所以可以对一些信息预处理。处理dis[x][y][k][h] ,代表x,y这个点的k方向上有空格,它想要走到h方向的最小步数。这东西可以n^2×4×4次bfs预处理。然后状态是dis[x][y][k]。状态之间转移一下,就变成了Spfa。
每次从起点跑到终点,统计下答案。注意数据中有起点终点相同的情况。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,qq;
bool mp[35][35];
int ex,ey;
struct Point{
    int x,y,b;
}tt[10005];
queue<Point> Q;
int cx[]={0,1,0,-1,0};
int cy[]={0,0,1,0,-1};
bool vis[32][32];
bool check(int x,int y)
{return x>0&&x<=n&&y>0&&y<=m&&mp[x][y];}
void init()
{
    while(!Q.empty()) Q.pop();
    memset(vis,0,sizeof(vis));
}
int bfs(int x,int y,int k,int h)
{
    Point S;init();S.b=0;
    S.x=x+cx[k];S.y=y+cy[k];
    int Ex=x+cx[h],Ey=y+cy[h];
    Q.push(S);
    while(!Q.empty())
    {
        Point B=Q.front();Q.pop();
        int nx=B.x,ny=B.y;
        if(nx==Ex&&ny==Ey) return B.b;
        for(int i=1;i<=4;i++)
        {
            int x=nx+cx[i],y=ny+cy[i];
            if(check(x,y)&&!vis[x][y])
            {
                Point C=B;C.b++;
                C.x=x;
                C.y=y;
                vis[C.x][C.y]=1;
                Q.push(C);
            }
        }
    }
    return -1;
}
queue<int> q;
int dis[35][35][5][5];
int id[35][35][5];
int inf=1e9+7,T=0;
int cst[10005];
int f(int i)
{
    if(i==1) return 3;
    if(i==2) return 4;
    if(i==3) return 1;
    return 2;
}
void bfs2(int x,int y,int ex,int ey)
{
    init();vis[x][y]=1;vis[ex][ey]=1;

    Point S;S.x=x;S.y=y;S.b=0;
    Q.push(S);
    while(!Q.empty())
    {
        Point A=Q.front();Q.pop();
        int nx=A.x,ny=A.y;
        for(int i=1;i<=4;i++)
        {
            if(ex+cx[i]==nx&&ey+cy[i]==ny)
            {
                int v=id[ex][ey][i];
                q.push(v);cst[v]=A.b;
                break;
            }
        }
        for(int i=1;i<=4;i++)
        {
            x=nx+cx[i];y=ny+cy[i];
            if(check(x,y)&&!vis[x][y])
            {
                Point B;vis[x][y]=1;
                B=(Point){x,y,A.b+1};
                Q.push(B);
            }
        }
    }
}
bool inq[10005];
int spfa(int Ex,int Ey)
{
    int ans=inf;
    while(!q.empty())
    {
        int v=q.front();q.pop();inq[v]=0;
        int x=tt[v].x,y=tt[v].y,k=tt[v].b;
        for(int i=1;i<=4;i++)
        {
            int X=x+cx[i],Y=y+cy[i];
            if(check(X,Y))
            {
                int Id=id[X][Y][f(i)];
                int vv=id[x][y][k];
                if(cst[Id]>cst[vv]+dis[x][y][k][i])
                {
                    cst[Id]=cst[vv]+dis[x][y][k][i];
                    if(X==Ex&&Y==Ey) ans=min(ans,cst[Id]);
                    if(!inq[Id])
                    {
                        q.push(Id);
                        inq[Id]=1;
                    }
                }
            }
        }
    }
    if(ans==inf) ans=-1;
    return ans;
}
int main()
{
    freopen("puzzle20.in","r",stdin);
    freopen("puzzle.ans","w",stdout);
    scanf("%d%d%d",&n,&m,&qq);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        scanf("%d",&mp[i][j]);
        for(int h=1;h<=4;h++)
        for(int k=1;k<=4;k++)
            dis[i][j][h][k]=inf;
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    for(int k=1;k<=4;k++)
        if(mp[i][j]&&check(i+cx[k],j+cy[k]))
        {
            id[i][j][k]=++T;
            tt[T].x=i;
            tt[T].y=j;
            tt[T].b=k;
        }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        if(mp[i][j])
        {
            mp[i][j]=0;
            for(int k=1;k<=4;k++)
            {
                if(mp[i+cx[k]][j+cy[k]])
                {
                    for(int h=1;h<=4;h++)
                    {
                        if(mp[i+cx[h]][j+cy[h]])
                        {
                            int w=bfs(i,j,k,h);
                            if(w==-1) continue;
                            dis[i][j][k][h]=w+1;
                        }
                    }
                }
            }
            mp[i][j]=1;
        }
    }
    while(qq--)
    {
        int x1,x2,y1,y2;
        scanf("%d%d",&x1,&y1);//  
        scanf("%d%d",&x2,&y2);//  start
        scanf("%d%d",&ex,&ey);//  end
        for(int i=1;i<=T;i++)
            cst[i]=inf;
        bfs2(x1,y1,x2,y2);
        if(x2==ex&&y2==ey) puts("0");
        else cout<<spfa(ex,ey)<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值