飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个 N×M 的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有 Ai j 的费用和 Bi j 的弹射能力。并规定有相邻边的格子间距离是 1 。那么,任何飞飞侠都只需要在 (i,j) 支付 Ai j 的费用就可以任意选择弹到距离不超过 Bi j 的位置了。如下图(从红色街区交费以后可以跳到周围的任意蓝色街区.)
现在的问题很简单。有三个飞飞侠,分别叫做 X,Y,Z 。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你 3 个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。
输入的第一行包含两个整数 N 和 M ,分别表示行数和列数。接下来是 2 个 N×M 的自然数矩阵,为 Ai j 和 Bi j 最后一行六个数,分别代表 X,Y,Z 所在地的行号和列号。
第一行输出一个字符 X、Y 或者 Z 。表示最优集合地点。第二行输出一个整数,表示最小费用。如果无法集合,只输出一行 NO
4 4
0 0 0 0
1 2 2 0
0 2 2 1
0 0 0 0
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
2 1 3 4 2 2
Z
15
100% 1≤N,M≤150;0≤Ai j≤109;0≤Bi j≤1000
这道题是最短路,看出来了吧,当然我们考场上倒是有人打爆搜的,搜倒是挺快的,但是 WA 了很多点,写最短路建了边的话只有 50 分,因为这个边数是 N2∗M2 就是爆炸,要优化建边,倒是在考场上有人不建边裸跑 dijkstra 擦线过,稳啊。
我们把一个人从座椅上弹射,想成弹到云层上, Dis[i][j][k] ,表示在坐标为 (i, j) 高为 k 的最短距离,你从云层 (i,j,k) 的地方只能向 (i,j,k−1) , (i−1,j,k−1) , (i,j−1,k−1) , (i+1,j,k−1) , (i,j+1,k−1) 的位置移动,只有到地面上才能被弹起,而且只有在被弹起时才会产生花费,在云层上面走的时候不会产生费用,这种思想你可以想想成水往低处流,就像 Minecraft 的水源,实在不行你可以理解为有 k 的能量。这样就不用建边,转移方式固定,然后dijkstra,记得一定要 break,记得一定要清队列。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define mp make_pair
using namespace std;
inline int read() {
int i = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1; ch = getchar();
}
while(isdigit(ch)) {
i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
}
return i * f;
}
const int MAXN = 200 + 5;
int Able[4];
int a[MAXN][MAXN], b[MAXN][MAXN], n, m, posp[4], mh;
LL dis[MAXN][MAXN][MAXN * 2], ans1, ans2, ans3;
priority_queueint, int> > > q;
struct point {
int x, y;
};
point p[4];
inline int getnum(int x, int y) {
return (x - 1) * m + y;
}
inline pair<int, int> getpos(int num) {
int y = num % m; if(y == 0) y = m;
int x = (num - y) / m + 1;
return mp(x, y);
}
inline void init() {
for(int k = 0; k <= mh;++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
dis[i][j][k] = 1e17;
while(!q.empty()) q.pop();
}
pair<int, int> k;
#define kf k.first
#define ks k.second
inline bool judge(int x, int y) {
return x >= 1 && x <= n && y <= m && y >= 1;
}
inline void check(int x, int y, int z, int num) {
if(dis[kf + x][ks + y][z - 1] > dis[kf][ks][z] && judge(kf + x, ks + y))
dis[kf + x][ks + y][z - 1] = dis[kf][ks][z],
q.push(mp(-dis[kf + x][ks + y][z - 1], mp(num + m * x + y, z - 1)));
}
inline void dij(int pos) {
int cnt = 0;
int nx = p[pos].x, ny = p[pos].y, S = posp[pos];
init();
dis[nx][ny][0] = 0; q.push(mp(0, mp(S, 0)));
while(!q.empty()) {
pairint, int> > u = q.top(); q.pop();
pair<int, int> v = u.second;
k = getpos(v.first);
if(v.second == 0 && (
(kf == p[1].x && ks == p[1].y) ||
(kf == p[2].x && ks == p[2].y) ||
(kf == p[3].x && ks == p[3].y)) &&
++cnt == 3) break;
if(v.second == 0) {
int now = a[kf][ks], len = b[kf][ks];
if(dis[kf][ks][now] > dis[kf][ks][0] + len) {
dis[kf][ks][now] = dis[kf][ks][0] + len;
q.push(mp(-dis[kf][ks][now], mp(getnum(kf, ks), now)));
}
}
else {
check(0, 0, v.second, v.first);
check(-1, 0, v.second, v.first);
check(0, -1, v.second, v.first);
check(1, 0, v.second, v.first);
check(0, 1, v.second, v.first);
}
}
if(pos == 1) {
ans2 += dis[p[2].x][p[2].y][0],
ans3 += dis[p[3].x][p[3].y][0];
}
else if(pos == 2) {
ans1 += dis[p[1].x][p[1].y][0],
ans3 += dis[p[3].x][p[3].y][0];
}
else {
ans1 += dis[p[1].x][p[1].y][0],
ans2 += dis[p[2].x][p[2].y][0];
}
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) {
a[i][j] = read();
if(a[i][j] > 400) a[i][j] = 400;
mh = max(mh, a[i][j]);
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
b[i][j] = read();
for(int i = 1; i <= 3; ++i)
p[i].x = read(), p[i].y = read(), posp[i] = getnum(p[i].x, p[i].y);
dij(1), dij(2), dij(3);
int k = 0; LL ans = 1e17;
if(ans > ans1) ans = (LL)ans1, k = 1;
if(ans > ans2) ans = (LL)ans2, k = 2;
if(ans > ans3) ans = (LL)ans3, k = 3;
if(k == 1) cout<<"X"<<'\n'; else if(k == 2) cout<<"Y"<<'\n'; else cout<<"Z"<<'\n';
cout<'\n';
}