分层图最短路,就是在分层图上解决最短路问题
一般模型为:
在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线
常规思路:
想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次……k次
这样实际我们可以把这k个点想象成对应dp的不同的状态
dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线
我们用to表示要到达的点,x表示父亲节点,就有
dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j – 1])
因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态
如果我们消耗了免费过路权
dis[to][j] = min{dis[x][j – 1]}
如果我们没消耗免费过路权
dis[to][j] = min{dis[x][j] + val(x, to)}
这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏
也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权
到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次….用了k次
也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了
然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路,这样可能有点抽象
实际上把这想象成一个dp的过程是最好理解的
#include<cstdio> #include<iostream> #include<queue> #include<utility> #include<cstring> using namespace std; #define inf 0x3f3f3f struct fuck { int to, len, ne; }ed[100005]; int vis[10005][12]; int d[10005][12]; int head[10005]; int cnt = 0, n, m, k, st, en; void init() { memset(vis, 0, sizeof(vis)); for (int s = 0; s <= n; s++) { for (int w = 0; w <= k; w++) { d[s][w] = inf; } head[s] = -1; } cnt = 0; } void add(int from, int to, int len) { ed[cnt].to = to; ed[cnt].len = len; ed[cnt].ne = head[from]; head[from] = cnt++; } void spfa() { d[st][0] = 0; vis[st][0] = 1; queue< pair<int,int> >q; q.push(make_pair(st, 0)); while (!q.empty()) { pair<int, int>t = q.front(); q.pop(); vis[t.first][t.second] = 0; for (int s = head[t.first]; ~s; s = ed[s].ne) { if (d[t.first][t.second] + ed[s].len < d[ed[s].to][t.second]) { d[ed[s].to][t.second] = d[t.first][t.second] + ed[s].len; if (!vis[ed[s].to][t.second]) { vis[ed[s].to][t.second] = 1; q.push(make_pair(ed[s].to, t.second)); } } if (d[t.first][t.second] < d[ed[s].to][t.second+1]&&t.second<k) { d[ed[s].to][t.second+1] = d[t.first][t.second]; if (!vis[ed[s].to][t.second+1]) { vis[ed[s].to][t.second+1] = 1; q.push(make_pair(ed[s].to, t.second+1)); } } } } } int main() { while (~scanf("%d%d%d", &n, &m, &k)) { init(); scanf("%d%d", &st, &en); while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); add(b, a, c); } spfa(); printf("%d\n", d[en][k]); } return 0; }