uva10801
题目链接请戳 这里
解题思路
用Dijkstra或者SPFA解决。
主要问题是怎么建图和怎么处理换乘。
1.建图
对于每部电梯,经过的楼层两两都要连接,且有回边。
2.判断换乘
每条边上记录所用时间和用的哪部电梯。用一个数组choice[]保存每次到达楼层时之前乘坐的电梯,
对于楼层0需要特判。其实我觉得这个choice[]数组类似于用于打印最短路径的父指针数组fa[]。
有了这个数组,换乘就简单了,如果选择的路径的电梯于之前的电梯不同,那要判断:d[v[e]] > d[x] + w[e] + 60
如果相同,那么只需要判断:d[v[e]] > d[x] + w[e].
代码
Dijkstra和SPFA都只用了0ms,因为数据本来就不强。
首先是Dijkstra算法的代码
#include<stdio.h> #include<string.h> #include<queue> #include<vector> #include<stdlib.h> #define N 110 #define M 20000 #define INF 1e9 using namespace std; typedef pair<int, int> pii; int u[M], v[M], nex[M], first[M], w[M], s[M]; int d[N], cost[N]; priority_queue <pii, vector<pii>, greater<pii> > q; int n, k, m, choice[N], a[N]; char c[500]; void addedge(int x, int y, int l) { u[m] = x; v[m] = y; w[m] = cost[l] * abs(y - x); s[m] = l; nex[m] = first[u[m]]; first[u[m]] = m; m++; u[m] = y; v[m] = x; w[m] = cost[l] * abs(y - x); s[m] = l; nex[m] = first[u[m]]; first[u[m]] = m; m++; } int dijkstra() { for (int i = 0; i < N; i++) d[i] = INF; d[0] = 0; memset(choice, 0, sizeof(choice)); q.push(make_pair(d[0], 0)); while (!q.empty()) { pii u = q.top(); q.pop(); int x = u.second; int now_choice = choice[u.second]; if (u.first != d[x]) continue; for (int e = first[x]; e != -1; e = nex[e]) { if ((now_choice == 0 || s[e] == now_choice) && d[v[e]] > d[x] + w[e]) { d[v[e]] = d[x] + w[e]; q.push(make_pair(d[v[e]], v[e])); choice[v[e]] = s[e]; } else if( s[e] != now_choice && d[v[e]] > d[x] + w[e] + 60) { d[v[e]] = d[x] + w[e] + 60; q.push(make_pair(d[v[e]], v[e])); choice[v[e]] = s[e]; } } } return d[k]; } void solve() { while (~scanf("%d%d", &n, &k)) { for (int i = 1; i <= n; i++) scanf("%d", &cost[i]); getchar(); for (int i = 0; i < N; i++) first[i] = -1; m = 0; for (int i = 1; i <= n; i++) { int num = 0; gets(c); char *p = c; while (*p != '\0') { sscanf(p, "%d", &a[num++]); while(*p != ' ' && *p != '\0') p++; if (*p == ' ') p++; } for (int j = 0; j < num - 1; j++) for (int k = j + 1; k < num; k++) addedge(a[j], a[k], i); } int ans = dijkstra(); if (ans == INF) printf("IMPOSSIBLE\n"); else printf("%d\n", ans); } } int main() { solve(); return 0; }
然后是SPFA。从Dijkstra改为SPFA很简单。把优先队列改为普通队列,且增加一个数组来判断节点是否在队列中。
#include<stdio.h> #include<string.h> #include<queue> #include<vector> #include<stdlib.h> #define N 110 #define M 20000 #define INF 1e9 using namespace std; typedef pair<int, int> pii; int u[M], v[M], nex[M], first[M], w[M], s[M]; int d[N], cost[N]; queue <int> q; int inq[N]; int n, k, m, choice[N], a[N]; char c[500]; void addedge(int x, int y, int l) { u[m] = x; v[m] = y; w[m] = cost[l] * abs(y - x); s[m] = l; nex[m] = first[u[m]]; first[u[m]] = m; m++; u[m] = y; v[m] = x; w[m] = cost[l] * abs(y - x); s[m] = l; nex[m] = first[u[m]]; first[u[m]] = m; m++; } int SPFA() { for (int i = 0; i < N; i++) d[i] = INF; d[0] = 0; memset(choice, 0, sizeof(choice)); memset(inq, 0, sizeof(inq)); q.push(0); inq[0] = true; while (!q.empty()) { int x = q.front(); q.pop(); int now_choice = choice[x]; inq[x] = false; for (int e = first[x]; e != -1; e = nex[e]) { if ((now_choice == 0 || s[e] == now_choice) && d[v[e]] > d[x] + w[e]) { d[v[e]] = d[x] + w[e]; if (!inq[v[e]]) { q.push(v[e]); inq[v[e]] = true; } choice[v[e]] = s[e]; } else if( s[e] != now_choice && d[v[e]] > d[x] + w[e] + 60) { d[v[e]] = d[x] + w[e] + 60; if (!inq[v[e]]) { q.push(v[e]); inq[v[e]] = true; } choice[v[e]] = s[e]; } } } return d[k]; } void solve() { while (~scanf("%d%d", &n, &k)) { for (int i = 1; i <= n; i++) scanf("%d", &cost[i]); getchar(); for (int i = 0; i < N; i++) first[i] = -1; m = 0; for (int i = 1; i <= n; i++) { int num = 0; gets(c); char *p = c; while (*p != '\0') { sscanf(p, "%d", &a[num++]); while(*p != ' ' && *p != '\0') p++; if (*p == ' ') p++; } for (int j = 0; j < num - 1; j++) for (int k = j + 1; k < num; k++) addedge(a[j], a[k], i); } int ans = SPFA(); if (ans == INF) printf("IMPOSSIBLE\n"); else printf("%d\n", ans); } } int main() { solve(); return 0; }