UVA10801 Lift Hopping 解题报告

UVA10801 Lift Hopping 解题报告

题目链接

https://vjudge.net/problem/UVA-10801

题目大意

在一个假想的大楼里,有编号为0~99的100层楼,还有n(n≤5)座电梯。你的任务是从第0楼到达第k楼。每个电梯都有一个运行速度,表示到达一个相邻楼层需要的时间(单位:秒)。由于每个电梯不一定每层都停靠,有时需要从一个电梯换到另一个电梯。换电梯时间总是1分钟,但前提是两座电梯都能停靠在换乘楼层。大楼里没有其他人和你抢电梯,但你不能使用楼梯(这是一个假想的大楼,你无须关心它是否真实存在)。

例如,有3个电梯,速度分别为10、50、100,电梯1停靠0、10、30、40楼,电梯2停靠0、20、30楼,电梯3停靠第0、20、50楼,则从0楼到50楼至少需要3920秒,方法是坐电梯1到达30楼(300秒),坐电梯2到达20楼(500秒+换乘60秒),再坐电梯3到达50楼(3000秒+换乘60秒),一共300+50+60+3000+60=3920秒。

解题思路

将每部电梯能够到达的楼层分别作为顶点来建图,将第i号电梯第x层编号为i * 100 + x。同一部电梯的所有顶点两两之间建边,权值为层数差乘以10,将不同电梯能够到达的相同楼层节点也建边(即电梯换乘),权值为60。建边后跑Dijkstra求单源最短路径即可。

由于本题在0层可以选择的电梯可能不止一部,那么起点可能也就不止一个,我们当然可以选择枚举起点算各自的单源最短路再找最小值,但这样不太优雅,比较低效(本题数据量不算大,这个方案也说不定能AC),我的方式是新建一个顶点编号为0,从0向所有的起点连一条边,将顶点0作为新的起点计算单源最短路,这样不会影响答案,还可以做到只跑一次Dijkstra。

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
const int maxn = 550;
const int INF = 0x3fffffff;
const int mod = 1e9 + 7;
struct edge {
    int v, w;
    bool operator < (const edge &other) const {
        return w > other.w;
    }
};
vector<edge> G[maxn];
vector<int> lift[6]; // 每一部电梯可以到达的楼层
int tim[6], d[maxn];
bool vis[maxn];
int n, k;

void init() {
    fill(d, d + maxn, INF);
    memset(tim, 0, sizeof tim);
    memset(vis, 0, sizeof vis);
    for (int i = 0; i < maxn; i++) {
        G[i].clear();
    }
}

void buidGraph() {
    // 每部电梯自己的楼层之间建边
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < lift[i].size(); j++) {
            for (int k = j + 1; k < lift[i].size(); k++) {
                int u = i * 100 + lift[i][j];
                int v = i * 100 + lift[i][k];
                int w = (v - u) * tim[i];
                G[u].push_back({v, w});     // 警钟敲烂:这里的G[u].push_back({v, w})写成了G[u].push_back({u, w})wa了半小时
                G[v].push_back({u, w});
            }
        }
    }
    // 给所有的电梯零层节点添加一个公共的起点,方便我们后续一次性算出最短路,不影响最短路结果
    for (int i = 1; i <= n; i++) {
        G[0].push_back({i * 100, 0});
    }
    // 给停留在相同层的电梯之间建边
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            for (int k = 0; k < lift[i].size(); k++) {
                for (int l = 0; l < lift[j].size(); l++) {
                    int a = lift[i][k];
                    int b = lift[j][l];
                    if (a == b) {
                        int u = i * 100 + a;
                        int v = j * 100 + b;
                        G[u].push_back({v, 60});
                        G[v].push_back({u, 60});
                    }
                }
            }
        }
    }
}

void dijkstra() {
    priority_queue<edge> q;
    q.push({0, 0});
    d[0] = 0;
    while (!q.empty()) {
        int u = q.top().v;
        q.pop();
        if (vis[u])
            continue;
        vis[u] = true;
        for (int j = 0; j < G[u].size(); j++) {
            int v = G[u][j].v;
            int w = G[u][j].w;
            if (!vis[v] && d[u] + w < d[v]) {
                d[v] = d[u] + w;
                q.push({v, d[v]});
            }
        }
    }
}

void solve() {
    while (scanf("%d%d", &n, &k) == 2) {
        init();
        for (int i = 1; i <= n; i++) {
            scanf("%d", &tim[i]);
        }
        getchar();
        char s[1000];
        for (int i = 1; i <= n; i++) {
            lift[i].clear();
            gets(s);
            int p = 0;
            while (s[p] != '\0') {
                if (s[p] == ' ')
                    p++;
                int tmp = 0;
                while (s[p] != ' ' && s[p] != '\0') {
                    tmp = tmp * 10 + (s[p++] - '0');
                }
                lift[i].push_back(tmp);
            }
        }
        buidGraph();
        dijkstra();
        int ans = INF;
        for (int i = 1; i <= n; i++) {
            ans = min(ans, d[i * 100 + k]);
        }
        if (ans < INF)
            cout << ans << "\n";
        else
            cout << "IMPOSSIBLE\n";
    }
}

int main() {
    solve();
    return 0;
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值