专题:分层图

专题:分层图

拖了整整一个月,我终于来学习分层图了,原因是考一道 USACO 的题正解死分层图,

秉持着竟然有用,那我就来学学的原则,学习了分层图。

纵然,这确实是个好东西,但是局限性也比较明显 ,分层图的分层的意思是把图整体复制几遍,跨层走意味着使用了一次特殊机会。

但是,显然这对数据范围有严格要求,图的边和点乘以我的层数不能太大,不然就凉了。

 一般建完图以后就是跑一个最短路,这里有一个非常需要重视的地方,就是对最短路算法的选择,有负权的时候切记使用 SPFA,其他一律 Dijkstra,不然会超时 

例题

练完发现都是一个套路,就是路径中给你一些优惠,比如说 \(k\) 次机会让路免费,又或者是减半。其中只有第二题 最优贸易 需要根据题意转换一下思路。

这里放最后一题的代码,很好理解,基本就是一个建图思路。

然后注意如果题面中写到不一定要把 \(k\) 个优惠用完的话,那要记得把每一层的终点给连接起来。

#include<bits/stdc++.h>
using namespace std;
#define N 4500000
int n,m,k,fir[N],nex[N],tot=0,w[N],to[N];
int dis[N],vis[N];
void add(int x,int y,int z){
//	printf("%d %d\n",x,y);
	nex[++tot]=fir[x];
	fir[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
typedef pair<int,int> PII;
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	priority_queue<PII,vector<PII>,greater<PII> >p;
	p.emplace(0,1);
	while(!p.empty()){
		int u=p.top().second;
		p.pop();
		if(!vis[u]){
			vis[u]=1;
			for(int e=fir[u];e;e=nex[e]){
				int v=to[e];
				if(dis[v]>dis[u]+w[e]){
					dis[v]=dis[u]+w[e];
					p.emplace(dis[v],v);
				}
			}
		}
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1,x,y,z;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);add(y,x,z);
		for(int j=1;j<=k;j++){
			add(j*n+x,j*n+y,z);
			add(j*n+y,j*n+x,z);
			add((j-1)*n+x,j*n+y,0);
			add((j-1)*n+y,j*n+x,0);
		}
	}
	for(int i=1;i<=k;i++){
		add((i-1)*n+n,i*n+n,0);
	}
	dijkstra();
	printf("%d",dis[n*(k+1)]);
	return 0;
}
posted @ 2023-11-10 22:01  alloverzyt  阅读(11)  评论(0编辑  收藏  举报