noip 2013 华容道

/*双向bfs (得分和单项的一样多....)70*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 35
#define maxm 810010
using namespace std;
int n,m,q,g[maxn][maxn];
int X,Y,sx,sy,ex,ey,falg,vis[maxn][maxn];
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
int f[maxn][maxn][maxn][maxn],s[maxn][maxn][maxn][maxn];
struct node
{
    int kx,ky,xi,yi;
    node (int xxx, int yyy, int tt, int ss):kx(xxx), ky(yyy), xi(tt), yi(ss){};
};
queue<node>Q;
void Bfs()
{
    if(sx==ex&&sy==ey)
      {
          printf("0\n");
          falg=1;return;
      }
    while(!Q.empty())Q.pop();
    f[X][Y][sx][sy]=1;
    s[X][Y][sx][sy]=0;
    Q.push(node(X,Y,sx,sy));
    for(int i=0;i<4;i++)
      {
          int nx=xx[i]+ex;
          int ny=yy[i]+ey;
          if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny][ex][ey]==0)
             {
               f[nx][ny][ex][ey]=2;
            s[nx][ny][ex][ey]=0;
                Q.push(node(nx,ny,ex,ey));
          }
      }
    while(!Q.empty())
      {
          node t=Q.front();Q.pop();
          int x=t.kx,y=t.ky;
          int xs=t.xi,ys=t.yi;
          int T=f[x][y][xs][ys];
          int k=s[x][y][xs][ys];
          for(int i=0;i<4;i++)
          {
             int nx=x+xx[i];
                int ny=y+yy[i];
                xs=t.xi;ys=t.yi;
                if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny])
                  {
                      if(nx==xs&&ny==ys)xs=x,ys=y;
                if(f[nx][ny][xs][ys]==0)
                      {
                          f[nx][ny][xs][ys]=T;
                          s[nx][ny][xs][ys]=k+1;
                           Q.push(node(nx,ny,xs,ys));
                  }
                else if(f[nx][ny][xs][ys]!=T)
                  {
                    printf("%d\n",s[nx][ny][xs][ys]+k+1);
                    falg=1;return;
                  }
              }

          }
      }
}
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",&g[i][j]);
    while(q--)
      {
          scanf("%d%d%d%d%d%d",&X,&Y,&sx,&sy,&ex,&ey);
        falg=0;memset(f,0,sizeof(f));Bfs();
        if(falg==0)printf("-1\n");
      }
    return 0;
}
/*
后来看了别人的思路 机智啊!
本来bfs不超时的 nm*nm 但是加上q次询问就T了
问题就在这 我们重复的bfs太多了 能不能一次预处理好呢
但是考虑到每次的起点终点还有空格位置是在变化的
所以我们要与处理一些与这些无关的 但是对答案还有贡献的
考虑到目标棋子的移动必须先让空格移动到4周
所以我们把每个空格和目标棋子的相邻的组合作为状态
也就是有n*m*4个状态 每个状态不是相邻的 互相转移需要一定的步数
这个步数就使我们要预处理的东西
然后把每个状态映射成一个图 将目标棋盘转化成同样的状态 然后跑最短路
具体的细节看代码把
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 35
#define maxm 810010
using namespace std;
int n,m,q,g[maxn][maxn],num,head[maxm],kx,ky,sx,sy,ex,ey;
int f[maxn][maxn],dis[maxm],vis[maxm],ans;
struct node{int u,v,t,pre;}e[maxm];
int xx[4]={-1,1,0,0};//上下左右 和下面的对应
int yy[4]={0,0,-1,1};
void Init()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        scanf("%d",&g[i][j]);
}
void Add(int from,int to,int dis)
{
    num++;e[num].v=to;
    e[num].t=dis;
    e[num].pre=head[from];
    head[from]=num;
}
void Bfs(int X,int Y,int xi,int yi,int k)
{
    queue<int>qx,qy;
    while(!qx.empty())qx.pop();
    while(!qy.empty())qy.pop();
    memset(f,0,sizeof(f));
    qx.push(xi);qy.push(yi);
    f[xi][yi]=1;//判重+步数记录
    while(!qx.empty())
      {
          int x=qx.front();qx.pop();
          int y=qy.front();qy.pop();
          for(int i=0;i<4;i++)
            {
                int nx=x+xx[i];
                int ny=y+yy[i];
                if(nx==X&&ny==Y)continue;
                if(nx>0&&nx<=m&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny]==0)
                  {
                      qx.push(nx);qy.push(ny);
                      f[nx][ny]=f[x][y]+1;
              }
          }
      }
    if(k==4)return;
    for(int i=0;i<4;i++)
      {
          int nx=X+xx[i],ny=Y+yy[i];
          if(f[nx][ny]==0||(nx==xi&&ny==yi))continue;
          Add(X*30*4+Y*4+k,X*30*4+Y*4+i,f[nx][ny]-1);//一一映射
      }
    Add(X*30*4+Y*4+k,xi*30*4+yi*4+(k^1),1);//每组之间的状态连起来 这里的^ 就是左变变 上变下
}
void Ready()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {

            if(!g[i][j]) continue;
            if(g[i-1][j]) Bfs(i,j,i-1,j,0);
            if(g[i+1][j]) Bfs(i,j,i+1,j,1);
            if(g[i][j-1]) Bfs(i,j,i,j-1,2);
            if(g[i][j+1]) Bfs(i,j,i,j+1,3);
        }
}
void SPFA()
{
    queue<int>q;memset(dis,127/3,sizeof(dis));
    for(int i=0;i<4;i++)
      {
          int nx=sx+xx[i];
          int ny=sy+yy[i];
           if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny])
            {
              int k=sx*30*4+sy*4+i;
              q.push(k);vis[k]=1;dis[k]=f[nx][ny]-1;//转化成可表示的状态
            }
      }
    while(!q.empty())
      {
          int k=q.front();q.pop();vis[k]=0;
          for(int i=head[k];i;i=e[i].pre)
            if(dis[e[i].v]>dis[k]+e[i].t)
              {
                dis[e[i].v]=dis[k]+e[i].t;
                if(vis[e[i].v]==0)
                  {
                    vis[e[i].v]=1;q.push(e[i].v);
                }
            }
      }
}
int main()
{
    Init();
    Ready();
    for(int i=1;i<=q;i++)
      {
        scanf("%d%d%d%d%d%d",&kx,&ky,&sx,&sy,&ex,&ey);
        if(sx==ex&&sy==ey)
          {
              printf("0\n");continue;
          }
        Bfs(sx,sy,kx,ky,4);//把空格移到目标棋子附近
        SPFA();ans=dis[0];
        for(int i=0;i<4;i++)
           {
               int k=ex*30*4+ey*4+i;
                ans=min(ans,dis[k]);
           }
         if(ans==dis[0])printf("-1\n");
         else printf("%d\n",ans);
      }
    return 0;
}
时间: 2024-10-03 22:41:56

noip 2013 华容道的相关文章

NOIp 2013 Day2 解题报告

NOIp 2013 Day2 解题报告 1.   积木大赛 每次只要选取连续最大的一段区间即可. 继续归纳可得,答案为∑i=1nmax{0,hi-hi-1} 复杂度O(N) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable/

NOIP 2013 火车运输【Kruskal + 树链剖分】

NOIP 2013 火车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入描述 Input Description 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路.接下来 m 行每行 3 个整数 x.y.z,每两个整数之间用一个空格隔开,表示

NOIp 2013 Day1 解题报告

NOIp 2013 Day1 解题报告 1.   转圈游戏 不难看出答案就是(x+m*10k) mod n 用快速幂算法,复杂度O(log2k) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,m,x,

【CodeVS 3290】【NOIP 2013】华容道

http://codevs.cn/problem/3290/ 据说2013年的noip非常难,但Purpleslz学长还是AK了.能A掉这道题真心orz. 设状态$(i,j,k)$表示目标棋子在$(i,j)$这个位置,空格在紧贴着目标棋子的$k$方向,$0≤k<4$. 因为目标棋子要移动,空格肯定在它旁边.往空格的方向走一步,空格便出现在它另一边.对于这两个状态连边,边权为1. 为了使目标棋子向某一方向移动,需要目标棋子不动,空格从紧贴着目标棋子的某一方向移动到紧贴着目标棋子的另一个方向.对于固

【noip】华容道

---恢复内容开始--- 描述 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间. 小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的: 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的: 有些棋子是固定的,有些棋子则是可以移动的: 任何与空白的格子相邻(有公共的边)的格

bzoj 2013 华容道

比较难的题单独拉出来讲咯 华容道 描述 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间. 小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的: 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的: 有些棋子是固定的,有些棋子则是可以移动的: 任何与空白的格子相邻(有公共的

NOIP 2013

Prob.1 转圈游戏 找到循环节,然后快速幂.代码: #include<cstdio> #include<cstring> #include<iostream> using namespace std; int pos[1000005],vis[1000000]; int n,m,k,x,p,mod; int pow(int a,int b){ int now=1; while(b){ if(b&1) now=1ll*now*a%mod; a=1ll*a*a%

NOIP 2013 花匠

有多种方案,找拐点数目最简单O(n) 注意此题有相邻点价值一样,代码改变一点 1 #include <cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #include<algorithm> 5 int s,n,k,i,a,b; 6 int main() { 7 while(~scanf("%d%d",&n,&a)) { 8 for(i=1; i<n; ++i) {

NOIP 2013 货车运输

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z