P2939 [USACO09FEB]改造路[分层图最短路]

题意翻译

约翰一共有N)个牧场.由M条布满尘埃的小径连接.小径可 以双向通行.每天早上约翰从牧场1出发到牧场N去给奶牛检查身体.

通过每条小径都需要消耗一定的时间.约翰打算升级其中K条小径,使之成为高 速公路.在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为0.

请帮助约翰决定对哪些小径进行升级,使他每天从1号牧场到第N号牧场所花的时间最短

解析

提高练习场看到这题,瞄一眼,诶,这不是分层图裸题吗???


【啥是分层图】

分层图,简单来说,就是把一张图复制多次,分为多份,每层图代表一种异于其它层次的图的状态。在这些互相平行本无干扰的图中,我们加入一些关键边,相当于该图某种特定状态到另一种状态的转移。类似动态规划,在转移后,我们就无法回到之前的状态了。

【这题咋做】

把图分为k层,对于题述每条边,我们不仅要在每层图上连一次权值为(d)的边,构造分层图,还要在每层图之间连一次权值为(0)的边,以添加转移边。

如果最短路经过了一条权值为(0)的边,就相当于把一条路升级了,要升k次,我们就建k层图。显然,由于我们取的是最短路,不会存在重复升级的情况(想一想,为什么)。

参考代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 3000010
#define MOD 2520
#define E 1e-12
#define ll long long
using namespace std;
inline int read()
{
	int f=1,x=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
struct rec{
	int next,ver,edge;
}g[N<<1];
int head[N],tot,n,m,k;
ll d[N];
bool v[N];
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
inline void add(int x,int y,int val)
{
	g[++tot].ver=y,g[tot].edge=val;
	g[tot].next=head[x],head[x]=tot;
}
inline void dijkstra(int s)
{
	memset(v,0,sizeof(v));
	memset(d,0x3f,sizeof(d));
	d[s]=0;q.push(make_pair(0,s));
	while(q.size()){
		int x=q.top().second;q.pop();
		if(v[x]) continue;
		v[x]=1;
		for(int i=head[x];i;i=g[i].next){
			int y=g[i].ver,z=g[i].edge;
			if(d[y]>d[x]+z){
				d[y]=d[x]+z;
				q.push(make_pair(d[y],y));
			}
		}
	}
}
int main()
{
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read(),val=read();
		for(int j=0;j<=k;++j){
			add(u+j*n,v+j*n,val),add(v+j*n,u+j*n,val);
			add(u+j*n,v+j*n+n,0),add(v+j*n,u+j*n+n,0);
		}
	}
	dijkstra(1);
	printf("%lld
",d[(k+1)*n]);
	return 0;
}
原文地址:https://www.cnblogs.com/DarkValkyrie/p/11580201.html