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)(图中红色的格子)上。

    移动过程如下:

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

    要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(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\n",getAns());
156     return 0;
157 }

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

时间: 2024-12-17 07:27:22

NOIP2013 提高组day2 3 华容道 BFS的相关文章

2016.7.12 NOIP2013提高组 day2解题报告(未完成版)

考试马不停蹄地到来,昨天的程序还没改完,今天又考了day2,虽然没有昨天那么懵逼,但还是不尽如人意,现在还没讲题,我打算先自己看一次解题报告,争取加深理解,毕竟一位前辈说过,做一套题的质量取决于题本身的质量和你思考的程度. 考试总结: 1.数据分析推测可行算法很重要,要灵活掌握常用算法的时间复杂度: 2.对拍的方式要熟练,写对拍耗费的时间过多: 3.要加强代码实现的能力,比较突出的表现就是写200-300行多函数模拟或搜索的能力: 4.不要急于写过不完的程序,要多拿一点时间来分析数据,样例不够还

[部分题解]noip2013提高组Day2

积木大赛: 之前没有仔细地想,然后就直接暴力一点(骗点分),去扫每一高度,连到一起的个数,于是2组超时 先把暴力程序贴上来(可以当对拍机) 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 FILE *fin = fopen("block.in","r"); 5 FILE *fout= fopen("block.out","w&quo

【解题】noip2013提高组(day1+day2)

这套题,勾起了我无数美好的回忆←意思是该好好复习了... [day1] 一.转圈游戏 首先,第一题,在处理k的时候应该用快速幂算法. 大概就是下面这样,要注意的是:1.二分时要判断有无余数.2.先设数,在进行乘积运算,不然会递归两次=.= 1 int pow(int a,int pos) 2 { 3 if(pos==1) return a%t; 4 int temp=pow(a,pos/2); 5 if(pos%2==1) return (temp*temp*a)%t; 6 return (te

【DFS】【最短路】【spfa】【BFS】洛谷P2296 NOIP2014提高组 day2 T2 寻找道路

存反图,从终点dfs一遍,记录下无法到达的点. 然后枚举这些记录的点,把他们的出边所连的点也全部记录. 以上这些点都是无法在最短路中出现的. 所以把两个端点都没被记录的边加进图里,跑spfa.BFS什么的随意. 1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 #define N 10001 7 vector

2015 Noip提高组 Day2

P2678 跳石头 [题目背景] 一年一度的“跳石头”比赛又要开始了! [题目描述] 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石).在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点. 为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳 跃距离尽可能长.由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能 移走起点和终点的岩石

noip2013 提高组

T1 转圈游戏 题目传送门 果不其然 第一题还是模拟题 一波快速幂解决问题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int read(){ int ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c&

【未完成0.0】Noip2012提高组day2 解题报告

第一次写一套题的解题报告,感觉会比较长.(更新中Loading....):) 题目: 第一题:同余方程 描述 求关于x的同余方程ax ≡ 1 (mod b)的最小正整数解. 格式 输入格式 输入只有一行,包含两个正整数a, b,用一个空格隔开. 输出格式 输出只有一行,包含一个正整数x0,即最小正整数解.输入数据保证一定有解. 样例1 样例输入1 3 10 样例输出1 7 限制 每个测试点1s 提示 对于40%的数据,2 ≤b≤ 1,000: 对于60%的数据,2 ≤b≤ 50,000,000:

洛谷 P1969 积木大赛(NOIp2013提高组D2T1)

题目描述 春春幼儿园举办了一年一度的"积木大赛".今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成n块高度为 0 的积木).接下来每次操作,小朋友们可以选择一段连续区间[l, r],然后将第第 L 块到第 R 块之间(含第 L 块和第 R 块)所有积木的高度分别增加1. 小 M 是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少.但她不是一个勤于动手的孩子,所

NOIP 2013 提高组 day2 积木大赛 找拐点

积木大赛 描述 春春幼儿园举办了一年一度的“积木大赛”.今年比赛的内容是搭建一座宽度为 n 的大厦,大厦可以看成由 n 块宽度为1的积木组成,第??块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成 n 块高度为 0 的积木).接下来每次操作,小朋友们可以选择一段连续区间[L,R],然后将第 L 块到第 R 块之间(含第 L 块和第 R 块)所有积木的高度分别增加1. 小 M 是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少.但她不是一个勤于动手的孩