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;
}