[NOIp2013]华容道
Description
Luogu1979
给定一个棋盘,有一个格子是空的,其余格子都有棋子,其中一些棋子是固定的,现在要求出将一个棋子S移动到目标位置的步数。
Solution
这个题的暴力还是很好打的,一个标准的bfs(状态是空白格的位置和S的位置)就可以拿到70分,不过要注意如果是hash判重的话,不要hash碰撞啊。
然后就是一个很巧妙的转化了:注意到bfs的过程实际上是求出了从初状态到末状态在状态转移图上的最短路,可以转化成最短路问题。但是,由于我们的状态是\(O(n^2m^2)\)的,无法建出状态转移图。所以考虑精简状态,注意到只有S和空白格相邻的状态才是对S的移动有用的,那么可以定义状态为\(Sta_{i,j,k}\),表示S在\((i,j)\)这个位置,空格在S的\(k\)方向(上下左右分别对应0123)。这样状态数就降下来了,并且末状态就是\(Sta_{T_x, T_y, k}\)。我们再来思考转移,一个比较高效的转移方法是从\(Sta_{i,j,k}\)转移到空格在\((i,j)\)处,S在\((i,j)\)的四个相邻位置上,这样转移也是比较优的:计算量不大(bfs计算出空格从\(k\)这个方向移到另一个方向的代价,然后+1就是这个转移的代价),而且转移数比较少。还有一个问题,那就是初始的时候空格不一定在S旁边,那就bfs算出把空格移到S旁边的代价,连一条边就行。最后,在状态转移图上跑最短路就行了。
Code
满分代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define REP(i, j, k) for (int i = j; i <= k; ++i)
const int N = 35;
const int INF = 0x3f3f3f3f;
const int EM = N * N * 4 * 4;
const int VN = N * N * 4;
const int MOV[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
struct Node {
int x, y;
Node (int a=0, int b=0) : x(a), y(b) {}
};
int tot; // 状态数
int s[N][N], Sta[N][N][4], step[N][N], vis[N][N], Len[N][N][4][4];
// 原图, 状态, BFS辅助数组*2, 转移代价
int hd[VN], nxt[EM], to[EM], w[EM], cnt; // 邻接表存边
int n, m, q, ex, ey, sx, sy, tx, ty; // 同原题
int Vis[VN], Dis[VN]; // 跑最短路用的
int BFS(int sx, int sy, int tx, int ty) { // 计算把空格从(sx, sy)移到(tx, ty)代价。
if (sx == tx && sy == ty) return 0; // mark : was `sx == sy && tx == ty`
memset(step, 0, sizeof step);
memset(vis, 0, sizeof vis);
std::queue<Node> que;
que.push(Node(sx, sy));
while (!que.empty()) {
Node tmp = que.front(); que.pop();
if (vis[tmp.x][tmp.y]) continue;
vis[tmp.x][tmp.y] = 1;
if (tmp.x == tx && tmp.y == ty) return step[tmp.x][tmp.y];
REP(k, 0, 3) {
if (s[tmp.x+MOV[k][0]][tmp.y+MOV[k][1]]
&& !vis[tmp.x+MOV[k][0]][tmp.y+MOV[k][1]]) {
que.push(Node(tmp.x+MOV[k][0], tmp.y+MOV[k][1]));
step[tmp.x+MOV[k][0]][tmp.y+MOV[k][1]] = step[tmp.x][tmp.y] + 1;
}
}
}
return INF;
}
inline void adde(int x, int y, int z) {
cnt++;
to[cnt] = y;
w[cnt] = z;
nxt[cnt] = hd[x];
hd[x] = cnt;
}
struct node {
int p, d;
node (int a=0, int b=0) : p(a), d(b) {}
bool operator< (const node &x) const { return d > x.d; }
};
std::priority_queue<node> pq;
inline int dij(int s, int t) {
memset(Vis, 0, sizeof Vis);
memset(Dis, 0x3f, sizeof Dis);
pq.push(node(s, Dis[s] = 0));
while (!pq.empty()) {
node x = pq.top(); pq.pop();
if (Vis[x.p]) continue;
Vis[x.p] = 1;
for (int i = hd[x.p]; i; i = nxt[i]) if (!Vis[to[i]]) {
if (Dis[to[i]] > Dis[x.p] + w[i]) {
pq.push(node(to[i], Dis[to[i]] = Dis[x.p] + w[i]));
}
}
}
return Dis[t] == INF ? -1 : Dis[t];
}
int main() {
// 输入
scanf("%d%d%d", &n, &m, &q);
REP(i, 1, n) REP(j, 1, m) scanf("%d", &s[i][j]);
// 建立状态转移图
// 计算状态标号
REP(i, 1, n) REP(j, 1, m) if (s[i][j]) {
REP(k, 0, 3) if (s[i + MOV[k][0]][j + MOV[k][1]]) {
Sta[i][j][k] = ++tot;
}
}
// 计算转移代价
memset(Len, 0x3f, sizeof Len);
REP(i, 1, n) REP(j, 1, m) if (s[i][j]) {
s[i][j] = 0;
REP(k, 0, 3) if (Sta[i][j][k]) {
REP(h, 0, 3) if (Sta[i][j][h]) {
Len[i][j][k][h] = BFS(i+MOV[k][0], j+MOV[k][1], i+MOV[h][0], j+MOV[h][1]) + 1;
}
}
s[i][j] = 1;
}
// 建图
REP(i, 1, n) REP(j, 1, m) if (s[i][j]) {
REP(k, 0, 3) if (Sta[i][j][k]) {
REP(h, 0, 3) if (Sta[i][j][h]) {
if (Len[i][j][k][h] <= INF) {
adde(Sta[i][j][k], Sta[i+MOV[h][0]][j+MOV[h][1]][h^1], Len[i][j][k][h]);
}
}
}
}
// 处理询问
while (q--) {
scanf("%d%d%d%d%d%d", &ex, &ey, &sx, &sy, &tx, &ty);
if (sx == tx && sy == ty) {
puts("0");
continue;
}
int S = ++tot, T = ++tot;
s[sx][sy] = 0;
REP(k, 0, 3) if (Sta[sx][sy][k]) {
int len = BFS(ex, ey, sx+MOV[k][0], sy+MOV[k][1]);
if (len < INF) adde(S, Sta[sx][sy][k], len);
}
REP(k, 0, 3) if (Sta[tx][ty][k]) {
adde(Sta[tx][ty][k], T, 0);
}
s[sx][sy] = 1;
printf("%d\n", dij(S, T));
}
return 0;
}
70分暴力:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
const int N = 35;
const int M = 1e6;
const int MOV[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int s[N][N];
int tx, ty, n, m, q, HA;
struct State {
int sx, sy, ex, ey, ha;
State(int a=0, int b=0, int c=0, int d=0) : sx(a), sy(b), ex(c), ey(d), ha(0) {}
int hash() { // 注意不要哈希碰撞(其实可以不哈希,用四维数组判重)
if (ha) return ha;
ha = ((sx * HA + sy) * HA + ex) * HA + ey;
return ha;
}
};
std::queue<State> que;
int vis[M], step[M];
int main() {
scanf("%d%d%d", &n, &m, &q);
HA = std::max(n, m) + 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &s[i][j]);
}
}
while (q--) {
memset(vis, 0, sizeof vis);
memset(step, 0, sizeof step);
while (!que.empty()) que.pop();
State sta;
scanf("%d%d%d%d%d%d", &sta.ex, &sta.ey, &sta.sx, &sta.sy, &tx, &ty);
que.push(sta);
int tot = 0, ans = -1;
while (!que.empty()) {
tot++;
State tmp = que.front(); que.pop();
if (vis[tmp.hash()]) continue;
if (tmp.sx == tx && tmp.sy == ty) {
ans = step[tmp.hash()];
break;
}
vis[tmp.hash()] = 1;
for (int i = 0; i < 4; ++i) {
if (!s[tmp.ex + MOV[i][0]][tmp.ey + MOV[i][1]]) continue;
if (tmp.ex + MOV[i][0] == tmp.sx && tmp.ey + MOV[i][1] == tmp.sy)
State sta = State(tmp.ex, tmp.ey, tmp.sx, tmp.sy);
else
State sta = State(tmp.sx, tmp.sy, tmp.ex+MOV[i][0], tmp.ey+MOV[i][1]);
if (!vis[sta.hash()]) {
que.push(sta);
step[sta.hash()] = step[tmp.hash()] + 1;
}
}
}
printf("%d\n", ans);
}
return 0;
}