P6233 [eJOI2019]Awesome Arrowland Adventure
比较容易的最短路。
主要考虑如何建图,可以发现每个点只要不是 X,那么它可以和与它四联通的每个点建边,那么边权是多少呢?
设目前点为 ,目标点为 , 是与 四联通的一个点,那么可得边权应该是目前点到目标点需要旋转的次数。
那么如果一个点为 X 怎么办?有两种做法,一种是仍然建边,不过边权是 ,另一种是不建边,这两种都可行,但第二种更优一些。
建完图后,可以发现,边权并不完全相同,考虑最短路或者 01-BFS,实测 01-BFS 快一些。
另外,网格图最短路建议选择 Dijkstra,SPFA 复杂度会很高。
Dijkstra 代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
constexpr int N(505), M(1e6 + 5);
int n, m;
char c[N][N];
int dx[] = { -1, 1, 0, 0 }; // 上下左右
int dy[] = { 0, 0, -1, 1 };
#define get(x) (x == 'W' ? 2 : (x == 'E' ? 3 : (x == 'N' ? 0 : 1)))
#define change(x) (x == 'W' ? 'N' : (x == 'E' ? 'S' : (x == 'N' ? 'E' : 'W')))
#define cg(x, y) ((x - 1) * m + y)
struct Edge
{
int to, cost;
Edge(int _to, int _cost): to(_to), cost(_cost){}
};
vector<Edge> G[M];
int dis[M];
bool f[M];
struct Node
{
int place, dist;
Node(int _p, int _d): place(_p), dist(_d){}
bool operator<(const Node& g) const
{
return dist > g.dist;
}
};
inline void dijkstra()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
priority_queue<Node> q;
q.push(Node(1, 0));
while (q.size())
{
int l = q.top().place;
q.pop();
if (f[l]) continue;
f[l] = true;
for (register int i(0), j(G[l].size()); i < j; ++i)
{
register int nx(G[l][i].to);
if (dis[nx] > dis[l] + G[l][i].cost)
{
dis[nx] = dis[l] + G[l][i].cost;
if (!f[nx]) q.push(Node(nx, dis[nx]));
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (register int i(1); i <= n; ++i)
{
for (register int j(1); j <= m; ++j)
{
scanf(" %c", &c[i][j]);
if (c[i][j] != 'X')
{
int p(get(c[i][j])), k(cg(i, j)), nx(i + dx[p]), ny(j + dy[p]);
char h(c[i][j]);
if (nx >= 1 and nx <= n and ny >= 1 and ny <= m)
{
G[k].push_back(Edge(cg(nx, ny), 0)); // 原始方向
}
for (register int bn(1); bn <= 3; ++bn)
{
h = change(h);
p = get(h);
nx = i + dx[p], ny = j + dy[p];
if (nx >= 1 and nx <= n and ny >= 1 and ny <= m) G[k].push_back(Edge(cg(nx, ny), bn));
}
}
}
}
dijkstra();
printf("%d\n", dis[cg(n, m)] == dis[0] ? -1 : dis[cg(n, m)]);
return 0;
}
01-BFS 代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
constexpr int N(505), M(1e6 + 5);
int n, m;
char c[N][N];
int dx[] = { -1, 1, 0, 0 }; // 上下左右
int dy[] = { 0, 0, -1, 1 };
#define get(x) (x == 'W' ? 2 : (x == 'E' ? 3 : (x == 'N' ? 0 : 1)))
#define change(x) (x == 'W' ? 'N' : (x == 'E' ? 'S' : (x == 'N' ? 'E' : 'W')))
#define cg(x, y) ((x - 1) * m + y)
struct Edge
{
int to, cost;
Edge(int _to, int _cost): to(_to), cost(_cost){}
};
vector<Edge> G[M];
int dis[M];
inline void bfs()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
deque<int> q;
q.push_back(1);
while (q.size())
{
int l = q.front();
q.pop_front();
for (register int i(0), j(G[l].size()); i < j; ++i)
{
register int nx(G[l][i].to);
if (dis[nx] > dis[l] + G[l][i].cost)
{
dis[nx] = dis[l] + G[l][i].cost;
if (G[l][i].cost == 0) q.push_front(nx);
else q.push_back(nx);
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (register int i(1); i <= n; ++i)
{
for (register int j(1); j <= m; ++j)
{
scanf(" %c", &c[i][j]);
if (c[i][j] != 'X')
{
int p(get(c[i][j])), k(cg(i, j)), nx(i + dx[p]), ny(j + dy[p]);
char h(c[i][j]);
if (nx >= 1 and nx <= n and ny >= 1 and ny <= m)
{
G[k].push_back(Edge(cg(nx, ny), 0)); // 原始方向
}
for (register int bn(1); bn <= 3; ++bn)
{
h = change(h);
p = get(h);
nx = i + dx[p], ny = j + dy[p];
if (nx >= 1 and nx <= n and ny >= 1 and ny <= m) G[k].push_back(Edge(cg(nx, ny), bn));
}
}
}
}
bfs();
printf("%d\n", dis[cg(n, m)] == dis[0] ? -1 : dis[cg(n, m)]);
return 0;
}
最后说一个技巧,大家看代码可以发现,我的代码有一个 #define cg(x, y) ((x - 1) * m + y),这是什么意思呢?不知道大家发现没有,这道题是一个二维的平面,我们最短路上的 dis 数组应该是二维,但是我的为什么是一维的呢?我们有一个办法,可以将坐标 转化为一个唯一的值,其实就是以这个点为右下角,以 为左上角的矩形包括的点的个数,显然这个值是唯一的。

浙公网安备 33010602011771号