第一次发紫题题解,居然在发布前太激动,把刚写好的还没发布的题解一个Ctrl+A和Backspace全删了。(所以这是二稿)
前置:
做本题一定要有的一些思想:
1、从简思想: 模拟白格子的移动,而千万不要想这去模拟众棋子的移动。这样会简单很多,否则会s的很惨。
2、转换思想(万物皆有其对立面):题目中给的规则,是棋子可以移动到白色格子上。我们将其共轭一下:
白色格子可以移动到棋子上,或者说,白色格子可以和棋子交换位置。
3、白色格子永远只有一个。(看似是废话的大实话)
4、习惯把起点叫做x,终点叫作ht
暴力那些事:
暴力不难实现,就是令空白格子到处移动,然后不断对ans取min就好了。别的题解有很多详细介绍,
在此不过多赘述。
思路获取:
First,从题目基本的信息入手。
如何才能令我们的目标棋子移动?
稍作思考。。。。。。当然是让空白格子在目标旗子旁边啊!!
一个要点get。
Second, 对白色格子全局的移动进行试验:
发现,白色格子的移动可以大致分为两个阶段:不顾一切移向目标棋子,然后在棋子周围打转(因为这样才可以步数最少)
だから(So),我们重点关注的对象,自然就来到了第二阶段上。
不难发现,无论棋子在哪个坐标上,白色格子永远都只会有四种情况:上,下,左,右。
移动后呢? 发现: 从一次移动到下一次移动,白色格子和棋子只会出现两大类移动:
1、白色格子还在棋子周围,只不过是上下左右随机。
2、白色格子直接和棋子位置进行了交换。
再看题目要求:最短路径。
那么,对于第一种情况,我们完全可以用BFS,求出白色格子从上一次位置移动到目前位置的距离。
第二种情况,步数很明显是1.
又一个要点get.
现在考虑,如何对我们的状态进行记录?
考虑用三维数组ok[x][y][k] (坐标x,y + 状态编号k)记录。 (其中k:k = 0,1,2,3,分别对应上下左右即可)
最后,再看一下题目的数据范围:我们完全可以求出(以棋子为中心)所有坐标到相邻坐标的步数。
!!!关系如此密集,何不考虑建图呢?
如果将所有的状态看做一个点,那么他到下一个状态的边权就是他们的步数。如果我们对这个图进行SPFA呢?
在那一串dist中,我们要求的距离,不正是终点ht周围的4个状态对应的dist吗!(取min,废话)
重大要点get!!
At last, 如何对这些状态进行编号,跑SPFA?不能直接用三维数组建图啊!
你需要一个公式:((x - 1) * m + y) * 4 - (4 - k);
至此,这道题也是被我们攻陷了。
后置:
1、一定要注意代码中的细节操作(特别是BFS跑状态的时候)
2、k对应的状态可千万不能忘呀,对应错了后果很严重呀!
码力全开!
#include <bits/stdc++.h> using namespace std; #define N 50 #define N2 5005 #define isdigit(c) ((c)>='0'&&(c)<='9') #define INF (~0u>>1) const int orz = 0; inline int read(){ int x = 0, s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = (x << 1) + (x << 3) + (c ^ '0'); c = getchar(); } return x * s; } bool ma[N][N], vis[N][N]; int fx[4] = {-1, 1, 0, 0}; int fy[4] = {0, 0, -1, 1}; int n, m, ex, ey, sx, sy, htx, hty; inline bool judge(int x, int y){ if(x < 1 || y < 1 || x > n || y > m)return 0; return ma[x][y] ; } inline int getnum(int x, int y, int t){ return ((x - 1) * m + y) * 4 - (4 - t); } struct data{ int x ,y; int step; /*存储每个坐标的信息*/ }; /* sx,sy是否可以到达htx, hty?*/ int bfs(int dx, int dy, int sx, int sy, int htx, int hty){ queue <data> q; /*模拟可移动格子的移动*/ memset(vis, 0, sizeof(vis)); q.push((data){sx, sy, 0}); vis[sx][sy] = 1; /*从起点开始*/ while(!q.empty()){ data now = q.front(); q.pop(); int x = now.x, y = now.y, step = now.step; if(x == htx && y == hty){ return now.step; /*如果到达终点*/ } for(int i = 0;i < 4; i++){ int l = x + fx[i], r = y + fy[i]; if(judge(l, r)){ if(vis[l][r] || (l == dx && r == dy))continue; /*如果新点已经访问过或者等于现在的*/ q.push((data){l, r, step + 1}); vis[l][r] = 1; } } } return INF; /*两个点不能互相到达*/ } struct node{ int u, v, w; int next; } t[N2]; int f[N2]; int bian = 0; void add(int u, int v, int w){ t[++bian].u = u; t[bian].v = v; t[bian].w = w; t[bian].next = f[u]; f[u] = bian; return ; } /*预处理, 找出所有可行状态*/ bool ok[N][N][5]; void prepare(){ for(int i = 1;i <= n; i++) for(int j = 1;j <= m; j++) if(ma[i][j]) for(int k = 0;k < 4; k++) if(judge(i+fx[k], j+fy[k])) ok[i][j][k] = 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 = k + 1; l < 4; l++) /*枚举不同的方向*/ if(ok[i][j][k] && ok[i][j][l]){ int a = getnum(i, j, k), b = getnum(i, j, l); int c = bfs(i, j, i + fx[k], j + fy[k], i + fx[l], j + fy[l]); if(c != INF){ /*状态之间是否可达*/ add(a, b, c); add(b, a, c); } } for(int i = 1;i <= n; i++) /*空白格子和棋子交换位置*/ for(int j = 1;j <= m; j++){ if(ok[i][j][3] && ok[i][j+1][2]){ int a = getnum(i, j, 3); /*几种小情况*/ int b = getnum(i, j+1, 2); add(a, b, 1); add(b, a, 1); } } for(int i = 1;i <= n; i++) for(int j = 1;j <= m; j++){ if(ok[i][j][1] && ok[i+1][j][0]){ int a = getnum(i, j, 1); int b = getnum(i+1, j, 0); add(a, b, 1); add(b, a, 1); } } return ; } /*进行最短路求解*/ queue <int> q; bool viss[N2]; int d[N2]; int spfa(){ memset(d, 127, sizeof(d)); memset(viss, 0, sizeof(viss)); int flag = d[2333]; /*之后判断用的*/ for(int i = 0;i < 4; i++){ /*先对起点进行选取*/ int l = sx + fx[i], r = sy + fy[i]; if(judge(l, r)){ int temp = bfs(sx, sy, ex, ey, l, r); /*ex,ey 是否可以到达 l,r? */ if(temp != INF){ int snum = getnum(sx, sy, i); viss[snum] = 1; q.push(snum); d[snum] = temp; } } } while(!q.empty()){ /*求所有状态间的最短路*/ int now = q.front(); q.pop(); viss[now] = 0; for(int i = f[now]; i;i = t[i].next){ int u = t[i].u, v = t[i].v, w = t[i].w; if(d[v] > d[u] + w){ d[v] = d[u] + w; if(!viss[v]){ viss[v] = 1; q.push(v); } } } } int ans = INF; for(int i = 0; i < 4; i++){ int num = getnum(htx, hty, i); ans = min(ans, d[num]); } return ans == flag ? -1 : ans; } int main(){ // freopen("hh.txt", "r", stdin); n = read(), m = read(); int T = read(); for(int i = 1;i <= n; i++) for(int j = 1;j <= m; j++) ma[i][j] = read(); prepare(); while(T--){ /*e: 空白 s: 起点 ht: 终点*/ ex = read(), ey = read(), sx = read(), sy = read(), htx = read(), hty = read(); if(sx == htx && sy == hty){ /*特判*/ puts("0"); continue; } if(!ma[htx][hty] || !ma[sx][sy]){ /*如果起点终点根本不在图内*/ puts("-1"); continue; } printf("%d ", spfa()); } return orz; /*向大佬势力低头 同时拜一下CCf求AC*/ }
(二稿真累)