题意:给定n个结点,m条边的无向连通图。
走过每条路径需要花费(w <= 50)的时间。
每次固定走两步,所耗费的时间为(w1 + w2)²。
输出1到其他所有点的最短时间的最小值。若没有则输出-1。
这道题目在网上有两种做法。其思想都是相同的。
参考连接:https://blog.csdn.net/weixin_45697774/article/details/113856213
https://www.cnblogs.com/irty/p/14416524.html
第一种:虚点连边+最短路
对于原图的边我们建立一张新的图。之后将_hash(u, 0)当作不为中间点的点。_hash(u, 1-50)当作作为中间点的点。当一个点连向作为中间点的点时,权值为0。当中间点连向目的点的时候权值为两个权值平方。
具体的描述应该如下:图片源自第二个博客。
对于每个点都有作为中间点和不作为中间点的情况。最暴力的思路应该就是在最短路里面去O(n2)去枚举两条边,但是n为2e5。这样的复杂度是我们所无法接受的。
所幸的是w只有50,那么我们就不枚举两条边。从而通过建虚点的方式获得上一条边的权值。
这里为了建点方便,就用了一个hash函数。因为权值只有50所以每个点乘上51+w肯定是不会冲突的。
具体的建图方式如上所述。
这里放上我的代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e7 + 5; #define pii pair<int, int> vector<pair<int, int> > G[maxn]; int dis[maxn]; int n, m; int _hash(int id, int w) { return id * 51 + w; } void _add(int u, int v, int w) { G[u].push_back({v, w}); } void add(int u, int v, int w) { _add(_hash(u, 0), _hash(v, w), 0); for (int i = 1; i <= 50; ++ i) { _add(_hash(u, i), _hash(v, 0), (i+w)*(i+w)); } } void dij(int S) { priority_queue<pii, vector<pii>, greater<pii> > pq; for (int i = 1; i <= maxn-1; ++ i) dis[i] = 0x3f3f3f3f; pq.push({dis[S] = 0, S}); while (!pq.empty()) { int u = pq.top().second; pq.pop(); for (auto i : G[u]) { int v = i.first; int w = i.second; if (dis[v] > dis[u] + w) { pq.push({dis[v] = dis[u] + w, v}); } } } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++ i) { int u, v; int w; scanf("%d%d%d", &u, &v, &w); add(u, v, w), add(v, u, w); } dij(_hash(1, 0)); for (int i = 1; i <= n; ++ i) { if (i != 1) printf(" "); if (dis[_hash(i, 0)] == 0x3f3f3f3f) { printf("-1"); } else printf("%d", dis[_hash(i, 0)]); } puts(""); return 0; }
(注意不需要开long long,开了反而会mle的)
第二种:多状态最短路
其实就是利用最短路贪心的思想进行的一个dp转移,开一个dis[点数][边权][是否为中间点]的数组。
每次就是有两个状态的推移:
中间点到非中间点:dis[next.id][next.w][0] = min(dis[pre.id][pre.w][1] + 边权)
非中间点到中间点:dis[next.id][next.w][1] = min(dis[pre.id][pre.w][0])
之后O(50n)来记录1到其他n个点,上一条边权为1-50且为非中间点的最短距离。即min(dis[1-n][1-50][0])
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 10; #define pii pair<int, int> vector<pair<int, int> > G[maxn]; int dis[maxn][51][2]; int n, m; void _add(int u, int v, int w) { G[u].push_back({v, w}); G[v].push_back({u, w}); } struct node { int id, pre, w, state; bool operator<(const node& p) const{ return w > p.w; } }; void dij() { priority_queue<node> pq; memset(dis, 0x3f, sizeof(dis)); pq.push({1, 0, dis[1][0][0] = 0, 0}); while (!pq.empty()) { node now = pq.top(); pq.pop(); int x = now.id; for (auto i : G[now.id]) { int v = i.first, w = i.second; int cost = 0; if (now.state == 1) { cost = (now.pre + w) * (now.pre + w); } if (dis[v][w][now.state^1] > dis[now.id][now.pre][now.state] + cost) { dis[v][w][now.state^1] = dis[now.id][now.pre][now.state] + cost; pq.push({v, w, dis[v][w][now.state^1], now.state^1}); } } } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++ i) { int u, v; int w; scanf("%d%d%d", &u, &v, &w); _add(u, v, w); } dij(); for (int i = 1; i <= n; ++ i) { int ans = 0x3f3f3f3f; for (int j = 0; j <= 50; ++ j) { ans = min(dis[i][j][0], ans); } if (i != 1) printf(" "); if (ans == 0x3f3f3f3f) printf("-1"); else printf("%d", ans); } puts(""); return 0; }