[NOIP模拟]BZOJ 2143 飞飞侠

3 篇文章 0 订阅
3 篇文章 0 订阅

Description

    飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个 N×M 的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有 Ai j 的费用和 Bi j 的弹射能力。并规定有相邻边的格子间距离是 1 。那么,任何飞飞侠都只需要在 (i,j) 支付 Ai j 的费用就可以任意选择弹到距离不超过 Bi j 的位置了。如下图(从红色街区交费以后可以跳到周围的任意蓝色街区.)
                                        这里写图片描述
    现在的问题很简单。有三个飞飞侠,分别叫做 XYZ 。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你 3 个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。

Input

输入的第一行包含两个整数 N M ,分别表示行数和列数。接下来是 2 N×M 的自然数矩阵,为 Ai j Bi j 最后一行六个数,分别代表 XYZ 所在地的行号和列号。

Output

第一行输出一个字符 XY 或者 Z 。表示最优集合地点。第二行输出一个整数,表示最小费用。如果无法集合,只输出一行 NO

Sample Input

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

Sample Output

Z
15

Hint

100% 1N,M150;0Ai j109;0Bi j1000

Source



Solution

    这道题是最短路,看出来了吧,当然我们考场上倒是有人打爆搜的,搜倒是挺快的,但是 WA 了很多点,写最短路建了边的话只有 50 分,因为这个边数是 N2M2 就是爆炸,要优化建边,倒是在考场上有人不建边裸跑 dijkstra 擦线过,稳啊。
    我们把一个人从座椅上弹射,想成弹到云层上, Dis[i][j][k] ,表示在坐标为 (i, j) 高为 k 的最短距离,你从云层(i,j,k) 的地方只能向 (i,j,k1) , (i1,j,k1) , (i,j1,k1) , (i+1,j,k1) , (i,j+1,k1) 的位置移动,只有到地面上才能被弹起,而且只有在被弹起时才会产生花费,在云层上面走的时候不会产生费用,这种思想你可以想想成水往低处流,就像 Minecraft 的水源,实在不行你可以理解为有 k <script type="math/tex" id="MathJax-Element-1462">k</script> 的能量。这样就不用建边,转移方式固定,然后dijkstra,记得一定要 break,记得一定要清队列。


Code :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
#include <queue>
#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_queue<pair<LL, pair<int, 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()) {
        pair<LL, pair<int, 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<<ans<<'\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值