分层图最短路,就是在分层图上解决最短路问题
一般模型为:
在一张图上,有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;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注