zoukankan      html  css  js  c++  java
  • NOIP2013 提高组day2 3 华容道 BFS

    描述

    小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。

    小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

    1. 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;

    2. 有些棋子是固定的,有些棋子则是可以移动的;

    3. 任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

    给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EXiEXi行第 EYiEYi 列,指定的可移动棋子的初始位置为第 SXiSXi 行第 SYiSYi 列,目标位置为第 TXiTXi 行第 TYiTYi 列。

    假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

    格式

    输入格式

    第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;

    接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。

    接下来的 q 行,每行包含 6 个整数依次是 EXiEXiEYiEYiSXiSXiSYiSYiTXiTXiTYiTYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

    输出格式

    输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。

    样例1

    样例输入1[复制]

     
    3 4 2 
    0 1 1 1 
    0 1 1 0 
    0 1 0 0 
    3 2 1 2 2 2 
    1 2 2 2 3 2
    

    样例输出1[复制]

     
    2 
    -1
    

    限制

    每个测试点1s。

    提示

    样例说明

    棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

    1. 第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。

      移动过程如下:

      img

    2. 第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。

      img

      要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。

    数据范围

    对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;
    对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;
    对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。

     

    解题报告

    看到这道题,又有一点小兴奋,因为是华容道这个游戏(好吧,好像是游戏我都很激动-.-),但是由于对BFS的不熟悉,(也就是“公式”还没有背到),知道要写BFS的我也无能为力。。。。所以顺带复习了一下BFS和增量数组。

    增量数组:zl[2][4]={{0,0,1,-1},{1,-1,0,0}};

    bfs:

     1 int bfs()
     2 {
     3     //初始化,初始状态存入队列             //或queue<int>q; 
     4     //队首指针 head=0;尾指针 tail=1;     // q.push(1)
     5     do                                     //while (!q.empty())
     6     {
     7         //指针head++; 指向待扩展结点       //int now=q.front(),q.pop()
     8         for (int i=1;i<=max;i++)
     9         {
    10             if (/*子节点符合条件*/)
    11             {
    12                 tail ++;//新节点存入队尾    //q.push(i)
    13                 if(/*新节点与原结点重复*/) 
    14                    //删去该节点(取消入队,tail--)
    15                  else
    16                    if(/*新节点是目标结点*/) //输出并退出 
    17                  
    18             }
    19         } 
    20     } while (head<tail);
    21 }

    啊,最后这道题还是只写了一个70分的代码,弃坑。(不不不,以后有时间还是要写的)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<cstring>
     5 #include<string>
     6 using namespace std;
     7 int n,m,q;
     8 int chess[35][35];
     9 const int zl[2][4]={{0,1,-1,0},{1,0,0,-1}};
    10 bool b[35][35][35][35];
    11 struct pp{
    12     int ex,ey,sx,sy;
    13     int step;
    14 };
    15 pp node[810005];
    16 bool pd(int x1,int y1,int x2,int y2)
    17 {
    18     if (x2<1||x2>n||y2<1||y2>m||!chess[x2][y2]) return false;
    19     if (b[x1][y1][x2][y2]) return false;
    20     b[x1][y1][x2][y2]=true;
    21     return true;
    22 }
    23 void bfs()
    24 {
    25     memset(b,false,sizeof(b));
    26     int tx,ty;
    27     scanf("%d%d%d%d%d%d",&node[0].ex,&node[0].ey,&node[0].sx,&node[0].sy,&tx,&ty);
    28     int head=0,tail=0;
    29     if (tx==node[0].sx&&ty==node[0].sy) {cout<<0<<endl;return ;}
    30     b[node[0].sx][node[0].sy][node[0].ex][node[0].ey]=true;
    31     node[tail].step=0;//初始化 
    32     while(head<=tail)
    33     {
    34         for (int i=0;i<=3;i++)
    35         {
    36             int x=node[head].sx,y=node[head].sy;
    37             int x1=node[head].ex+zl[0][i],y1=node[head].ey+zl[1][i];
    38             if (x==x1&&y==y1) {x=node[head].ex;y=node[head].ey;}/*!!!!*/
    39             if (pd(x,y,x1,y1))
    40             {
    41                 tail++;
    42                 node[tail].sx=x;node[tail].sy=y;//入队 
    43                 node[tail].ex=x1;node[tail].ey=y1;
    44                 node[tail].step=node[head].step+1;
    45                 if (x==tx&&y==ty) {cout<<node[tail].step<<endl;return ;}
    46             }
    47         }
    48         head++;
    49     }while (head<tail);
    50     cout<<-1<<endl;
    51     return ;
    52 }
    53 int main()
    54 {
    55     freopen("puzzle.in","r",stdin);
    56     freopen("puzzle.out","w",stdout);
    57     cin>>n>>m>>q;
    58     for(int i=1;i<=n;i++)
    59       for(int j=1;j<=m;j++)
    60         scanf("%d",&chess[i][j]);
    61     for (int i=1;i<=q;i++)
    62         bfs();
    63     return 0;
    64 }

    完整100代码是spfa 和 bfs:(复制版本)

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstdlib>
      4 #include <cstring>
      5 #include <queue>
      6 #define MaxN 35
      7 using namespace std;
      8 const int 
      9     INF=~0U>>2,
     10     dx[]={0,0,-1,1},
     11     dy[]={-1,1,0,0};//注意顺序,才易实现0变1,1变0;2变3,3变2 
     12 int mat[MaxN][MaxN],dis[MaxN][MaxN][4];
     13 bool vis[MaxN][MaxN][4];
     14 int step[MaxN][MaxN][4][4];
     15 int d[MaxN][MaxN];
     16 int n,m,q,test,ex,ey,sx,sy,tx,ty;
     17 struct node
     18 {
     19        int x,y;
     20 };
     21 struct node2
     22 {
     23        int x,y,k;
     24 };
     25 bool inside(int x, int y)
     26 {
     27     return (x>=1&&x<=n&&y>=1&&y<=m);
     28 }
     29 int spfa()
     30 {
     31     queue<node2> q;
     32     memset(vis,false,sizeof(vis));
     33     while(!q.empty()) q.pop();//局部队列,可能非空,所以清空 
     34     for(int k=0;k<4;k++)//把初始位置棋子与空格相邻,四个方向组成的四种可能的步数入队,当成四个源点。 
     35         if(dis[sx][sy][k]!=INF)
     36         {    
     37             q.push((node2){sx,sy,k});
     38             vis[sx][sy][k]=true;
     39         }
     40     while(!q.empty())
     41     {    
     42         int x=q.front().x;
     43         int y=q.front().y;
     44         int k=q.front().k;
     45         q.pop();
     46         vis[x][y][k]=false;
     47         for(int i=0;i<4;i++)
     48         {
     49             int _x=x+dx[i];//棋子(x,y)扩展的点(_x,_y)
     50             int _y=y+dy[i];
     51             if(inside(_x,_y))
     52             if(mat[_x][_y])
     53             if(step[x][y][k][i]!=INF)//棋子(x,y)k方向空格可以移到棋子(x,y)i方向。 
     54             if(dis[_x][_y][i^1]>dis[x][y][k]+step[x][y][k][i]+1) 
     55             {//棋子(x,y)k方向空格移到棋子(x,y)i方向,再把棋子移到空格上,所以+1,空格变成i^1方向。
     56                 dis[_x][_y][i^1]=dis[x][y][k]+step[x][y][k][i]+1;
     57                 if (not vis[_x][_y][i^1])
     58                  { 
     59                    q.push((node2){_x,_y,i ^ 1 });
     60                    vis[_x][_y][i^1]=true;
     61                  }
     62             }
     63         }
     64     }
     65     int ans=INF;
     66     for(int i=0;i<4;i++)
     67         if(dis[tx][ty][i]<ans)
     68             ans=dis[tx][ty][i];//找出目标位置与空格相邻四种情况最少步数 
     69     return (ans==INF)? -1:ans;
     70 }
     71 int bfs(int sx,int sy,int tx,int ty)//将空格从(sx,sy)移到(tx,ty)的步数 
     72 {
     73     if(!mat[sx][sy])
     74         return INF;
     75     if(!mat[tx][ty])
     76         return INF;
     77     for(int i=1;i<=n;i++)
     78         for(int j=1;j<=m;j++) 
     79             d[i][j]=INF;//INF可以做为没有到过的标志 
     80     d[sx][sy]=0;
     81     queue<node> q;//局部队列,可能非空,所以清空 
     82     while(!q.empty()) q.pop();
     83     q.push((node){sx,sy});
     84     while(!q.empty())
     85     {
     86         if(d[tx][ty]!=INF)
     87           return d[tx][ty];
     88         int x=q.front().x;
     89         int y=q.front().y;
     90         q.pop();
     91         for(int i=0;i<4;i++)
     92         {
     93             int _x=x+dx[i];
     94             int _y=y+dy[i];
     95             if(inside(_x,_y))
     96                 if(mat[_x][_y]&&d[_x][_y]==INF)
     97                 {
     98                     d[_x][_y]=d[x][y]+1;
     99                     //if(d[tx][ty]!=INF)
    100                     //   return d[tx][ty];
    101                     //与下面不等价,有可能(sx,sy)与(tx,ty)一样,所以最好放在出队前判断 
    102                     //if (_x==tx&&_y==ty) return d[tx][ty];????
    103                     q.push((node){_x,_y});
    104                 }
    105         }
    106     }
    107     return INF;
    108 }
    109 void init()
    110 {
    111     for(int i=1;i<=n;i++)
    112         for(int j=1;j<=m;j++)
    113         {
    114              int v=mat[i][j];
    115             mat[i][j]=0;//此位置可能是0或1,均改成0,因为移动空格不能移动棋子(i,j) 
    116             for (int k=0;k<4;k++)
    117                 for (int l=0;l<4;l++) 
    118                      step[i][j][k][l]=bfs(i+dx[k],j+dy[k],i+dx[l],j+dy[l]);
    119                 //step[i][j][k][l] 棋子(i,j)k方向相邻的空格移动到相邻的l方向的步数,
    120              mat[i][j]=v;//还原
    121         }
    122 }    
    123 int getAns()
    124 {
    125     scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
    126     if(sx==tx&&sy==ty)//初始位置与目标位置同,0步
    127         return 0;
    128     if(sx==ex&&sy==ey)//初始位置与空格位置同,无解
    129         return -1;
    130     if(!inside(ex,ey)||!inside(sx,sy)||!inside(tx,ty))//三个位置至少有一个在棋盘外
    131         return -1;
    132     if(!mat[ex][ey]||!mat[sx][sy]||!mat[tx][ty])////三个位置至少有一个是固定的
    133         return -1;
    134     for(int i=1;i<=n;i++)
    135         for (int j=1;j<=m;j++)
    136             for (int k=0;k<4;k++)
    137                 dis[i][j][k]=INF;
    138     mat[sx][sy]=0;//此时一定是1,改成0,因为前面已经判断过 
    139     for(int k=0;k<4;k++)
    140          dis[sx][sy][k]=bfs(ex,ey,sx+dx[k],sy+dy[k]);
    141     //dis[sx][sy][k]表示将空格移到(sx,sy)相邻并在k方向上的步骤 
    142     mat[sx][sy]=1; //还原 
    143     return spfa();
    144 }
    145 int main()
    146 {
    147     freopen("puzzle.in","r",stdin);
    148     freopen("puzzle.out","w",stdout);
    149     scanf("%d%d%d",&n,&m,&test);
    150     for(int i=1;i<=n;i++)
    151         for(int j=1;j<=m;j++)
    152             scanf("%d",&mat[i][j]);
    153     init();
    154     while(test--)
    155         printf("%d
    ",getAns());
    156     return 0;
    157 }

    啊,太长了,一定要好好消化。。。

  • 相关阅读:
    1.unix网络编程基础知识
    eclipse中如何获得feature与plugin的list
    eclipse中Debug的三种方式
    Eclipse Update Site中Nested Features问题
    java中URL,URLConnection,HttPURLConnection的使用
    iBatis简单入门教程
    Git 常用命令整理
    【整理】chmod和chown命令的用法
    【原创】Gson使用,json转换为java对象
    CSS reset 一份很全的样式表,附YUI的css reset
  • 原文地址:https://www.cnblogs.com/lx0319/p/5664964.html
Copyright © 2011-2022 走看看