zoukankan      html  css  js  c++  java
  • P1979 [NOIP]华容道

    luogu 传送门
    搞了半天,终于搞了出来。
    以前写过一个暴力70分的做法(点这里)

    在70分的基础上:
    注意到只有空格在指定棋子旁边才有意义,所以可以O(n^2)记录状态,记录空格在指定棋子的哪个方向。
    因为询问数比较多,所以可以预处理出来【指定棋子不动,空格移动到另一边】和【棋子与空格交换位置】两种情况,以移动步数为边权,以状态为点建图。
    代码中有解释

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #define M 40009
    #define INF 0x3f3f3f3f//赋0x7fffffff会爆 
    using namespace std;
    int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
    int head[M],nxt[M],to[M],cost[M],cnt,tot,dis[M];
    int a[35][35],num[35][35][5];//num[i][j][k]表示指定棋子在i,j这个位置,空白格子在k这个位置这个状态编号 
    int ex,ey,sx,sy,tx,ty,n,m,Q;
    bool vis[35][35],f[M];
    struct H{int x,y,step;}; 
    void add(int x,int y,int z)
    {
        to[++cnt]=y;
        nxt[cnt]=head[x];
        head[x]=cnt;
        cost[cnt]=z;
    }
    int bfs(int ax,int ay,int bx,int by,int cx,int cy)//不经过cx,cy 
    {
        if(ax==bx&&ay==by) return 0;
        memset(vis,0,sizeof(vis));
        queue <H> q;
        q.push((H){ax,ay,0});
        vis[ax][ay]=1;
        while(!q.empty())
        {
            H k=q.front();q.pop();
            if(k.x==bx&&k.y==by) return k.step;
            for(int i=0;i<4;i++)
            {
                int x=k.x+dx[i],y=k.y+dy[i];
                if(x>=1&&x<=n&&y>=1&&y<=m&&a[x][y]&&(x!=cx||y!=cy)&&!vis[x][y])
                {
                    q.push((H){x,y,k.step+1});
                    vis[x][y]=1;
                    if(x==bx&&y==by) return k.step+1;
                }
            }
        }
        return INF;
    } 
    int spfa()
    {
        queue <int> q;
        if(sx==tx&&sy==ty) return 0;
        for(int i=1;i<=tot;i++) dis[i]=INF;
        for(int k=0;k<4;k++)
         if(num[sx][sy][k]){
            dis[num[sx][sy][k]]=bfs(ex,ey,sx+dx[k],sy+dy[k],sx,sy);//将空白格子移到s格子周围 
            q.push(num[sx][sy][k]); 
            //f[num[sx][sy][k]]=1;
        }
        while(!q.empty())
        {
            int k=q.front();q.pop();
            f[k]=0;
            for(int i=head[k];i;i=nxt[i])
            {
                int t=to[i];
                if(dis[t]>dis[k]+cost[i])
                {
                    dis[t]=dis[k]+cost[i];
                    if(!f[t]) q.push(t),f[t]=1;
                }
            }
        }
        int ans=INF;
        for(int k=0;k<4;k++)
         if (num[tx][ty][k])
         ans=min(ans,dis[num[tx][ty][k]]);
        return ans==INF?-1:ans;
    }
    void init()
    {
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          for(int k=0;k<4;k++)
           if(a[i][j]&&a[i+dx[k]][j+dy[k]])
            num[i][j][k]=++tot;
    
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          for(int k=0;k<4;k++)
           if(num[i][j][k])//如果这个状态存在,那么交换位置后的状态也一定存在 
            add(num[i][j][k],num[i+dx[k]][j+dy[k]][k^1],1);//^1是相反方向,表示与空白格子交换位置边权为1
    
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          for(int k=0;k<4;k++)
           for(int l=0;l<4;l++)
            if(l!=k&&num[i][j][k]&&num[i][j][l])//空白格子从某个方向移动到另一方向 
             add(num[i][j][k],num[i][j][l],bfs(i+dx[k],j+dy[k],i+dx[l],j+dy[l],i,j));    
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&Q);
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
    
        init();
        while(Q--)
        {
            scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
            printf("%d
    ",spfa());
        }
        return 0;
    }
  • 相关阅读:
    第13组_16通信3班_045_OSPFv3作业
    RIPng配置(第十三组)
    基于IPV6的数据包分析(更新拓扑加入了linux主机和抓取133icmp包)(第十三组)
    vmware vsphere powercli 因为在此系统中禁止执行脚本
    vmware virtual machine must be running in order to be migrated
    flashback transaction闪回事务查询
    oracle 闪回功能详解
    linux下修改/dev/shm tmpfs文件系统大小
    vmware虚拟机guest系统重启后获得169.254.X.X的ip解决方法
    一键部署 PPTP server
  • 原文地址:https://www.cnblogs.com/dfsac/p/7587834.html
Copyright © 2011-2022 走看看