191028-分层图最短路

191028-分层图最短路

分层图

分层图最短路是指在可以进行分层图的图上解决最短路问题。分层图:可以理解为有多个平行的图。
一般模型是:在一个正常的图上可以进行 k 次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价。一般将决策前的状态和决策后的状态之间连接一条权值为决策代价的边,表示付出该代价后就可以转换状态了。

一般有两种方法解决分层图最短路问题:

1,建图时直接建成k+1层
2,多开一维记录机会信息。

第一种方法

第一种方法
例图

第二种方法

第二种方法
对于刚才的数据,也就会有下方的建图
例图
以上摘抄自 传送门

例题

T1 Telephone Lines

方法1,二分答案+spfa

解析

本题的的答案显然具有单调性,因为支付的钱更多时,合法的升级方案一定包含了花费更少的方案,所以我们可以将问题转换为:是否存在一条合法的升级方法,使花费不超过mid
因此每次二分出答案后,只需要把升级价格大于mid的电缆看做长度为1的路径,其余的看做长度为0的路径,然后跑一遍spfa,如果d[n]<=k 则返回true 否则返回false(另外针对边权只有0,1的最短路,也可以用双端bfs)

代码1
#include<bits/stdc++.h>
using namespace std;
int first[1001],to[20001],nxt[20001],w[20001],n,m,d[1001],tot,k;
bool vis[1001],bj;
void add(int x,int y,int z)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void spfa(int r)
{
	queue<int>q;
	memset(d,127,sizeof(d));
	memset(vis,0,sizeof(vis));
	d[1]=0;
	q.push(1);
	while(!q.empty())
	{
		int v=q.front();
		vis[v]=0;
		q.pop();
		for(int i=first[v];i;i=nxt[i])
		{
			int p=0;
			int u=to[i];
			if(w[i]>r) p=1;
			if(d[u]>d[v]+p)
			{
				d[u]=d[v]+p;
				if(!vis[u])
				{
					q.push(u);
					vis[u]=1;
				}
			}
		}
	}
}
bool check(int r)
{
	spfa(r);
	if(d[n]<=k) return 1;
	return 0;
}
int main()
{
	int x,y,z[10001];
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z[i]);
		add(x,y,z[i]);
		add(y,x,z[i]);
	}
	sort(z+1,z+m+1);
	int l=0;
	int r=m;
	while(l<r)
	{
		int mid=(l+r)/2;
		if(check(z[mid]))
		{
			r=mid;
			bj=1;
		}
		else
			l=mid+1;
	}
	if(!bj) printf("-1\n"); 
	else printf("%d",z[r]);
	return 0;
}

方法2 分层图最短路

解析

分层图一般用来解决特殊的最短路问题

代码2
#include<bits/stdc++.h>
#define M 3000009
using namespace std;
const long long INF=1000000009;
int nxt[M*2],first[M],to[M],w[2*M],d[M],n,m,tot,k;
bool vis[M];
priority_queue<pair<int,int> >q; 
void add(int x,int y,int z)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void dj()
{
	memset(d,127,sizeof(d));
	d[1]=0;
	vis[1]=1;
	q.push(make_pair(-d[1],1));
	while(!q.empty())
	{
		int v=q.top().second;
		q.pop();
		for(int i=first[v];i;i=nxt[i])
		{
			int u=to[i];
			if(d[u]>max(d[v],w[i]))//求的是第k+1大边的最小值
			{
				d[u]=min(d[u],max(d[v],w[i]));
				if(!vis[u])
					q.push(make_pair(-d[u],u));
			}
		}
	}
}
int main()
{
	int x,y,z;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		for(int j=0;j<=k;j++)
		{
			add(x+j*n,y+j*n,z);
			add(y+j*n,x+j*n,z);
			if(j!=k)
			{
				add(x+j*n,y+(j+1)*n,0);
				add(y+j*n,x+(j+1)*n,0);//分层图建边
			}
		}
	}
	dj();
	long long ans=INF;
	for(int i=0;i<=k;i++)
		ans=min(ans,(long long)d[n+i*n]);
	if(ans==INF)
		printf("-1\n");
	else
		printf("%lld\n",ans);
	return 0;
}

T2 飞行路线

分层图最短路代码

#include<bits/stdc++.h>
#define M 3000009
using namespace std;
int nxt[M*2],first[M],to[2*M],w[2*M],n,m,tot,k,s,t;
long long d[M];
bool vis[M];
priority_queue<pair<int,int> >q; 
void add(int x,int y,int z)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void dj(int r)
{
	memset(d,127,sizeof(d));
	d[r]=0;
	vis[r]=1;
	q.push(make_pair(-d[r],r));
	while(!q.empty())
	{
		int v=q.top().second;
		q.pop();
		for(int i=first[v];i;i=nxt[i])
		{
			int u=to[i];
			if(d[u]>d[v]+w[i])//求的是最短路
			{
				d[u]=d[v]+w[i];
				if(!vis[u])
					q.push(make_pair(-d[u],u));
			}
		}
	}
}
int main()
{
	int x,y,z;
	scanf("%d%d%d",&n,&m,&k);
	scanf("%d%d",&s,&t);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		for(int j=0;j<=k;j++)
		{
			add(x+j*n,y+j*n,z);
			add(y+j*n,x+j*n,z);
			if(j!=k)
			{
				add(x+j*n,y+(j+1)*n,0);
				add(y+j*n,x+(j+1)*n,0);//??
			}
		}
	}
	dj(s);
	long long ans=d[t];
	for(int i=1;i<=k;i++)
		ans=min(ans,(long long)d[t+i*n]);
	printf("%lld\n",ans);
	return 0;
}

T3 改造路

分层图最短路代码

#include<bits/stdc++.h>
#define M 3000090
using namespace std;
int nxt[M*2],first[M],to[2*M],w[2*M],n,m,tot,k,s,t;
long long d[M];
bool vis[M];
priority_queue<pair<int,int> >q; 
void add(int x,int y,int z)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void dj(int r)
{
	memset(d,127,sizeof(d));
	d[r]=0;
	vis[r]=1;
	q.push(make_pair(-d[r],r));
	while(!q.empty())
	{
		int v=q.top().second;
		q.pop();
		for(int i=first[v];i;i=nxt[i])
		{
			int u=to[i];
			if(d[u]>d[v]+w[i])
			{
				d[u]=d[v]+w[i];
				if(!vis[u])
					q.push(make_pair(-d[u],u));
			}
		}
	}
}
int main()
{
	int x,y,z;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		for(int j=0;j<=k;j++)
		{
			add(x+j*n,y+j*n,z);
			add(y+j*n,x+j*n,z);
			if(j!=k)
			{
				add(x+j*n,y+(j+1)*n,0);
				add(y+j*n,x+(j+1)*n,0);//??
			}
		}
	}
	dj(1);
	long long ans=d[n];
	for(int i=1;i<=k;i++)
		ans=min(ans,(long long)d[n+i*n]);
	printf("%lld\n",ans);
	return 0;
}

T4 冻结

代码

#include<bits/stdc++.h>
#define M 3000006
using namespace std;
int first[M],tot,nxt[M*2],to[M*2],w[M*2],d[M],n,m,k;
bool vis[M];
priority_queue<pair<int,int> >q; 
void add(int x,int y,int z)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void dj()
{
	memset(d,127,sizeof(d));
	d[1]=0;
	vis[1]=1;
	q.push(make_pair(-d[1],1));
	while(!q.empty())
	{
		int v=q.top().second;
		q.pop();
		for(int i=first[v];i;i=nxt[i])
		{
			int u=to[i];
			if(d[u]>d[v]+w[i])
			{
				d[u]=d[v]+w[i];
				if(!vis[u])
					q.push(make_pair(-d[u],u));
			}
		}
	}
}
int main()
{
	int x,y,z;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		for(int j=0;j<=k;j++)
		{
			add(x+j*n,y+j*n,z);
			add(y+j*n,x+j*n,z);
			if(j!=k)
			{
				add(x+j*n,y+(j+1)*n,z/2);
				add(y+j*n,x+(j+1)*n,z/2);
			}
		}
	}
	dj();
	int ans=d[n];
	for(int i=1;i<=k;i++)
		ans=min(ans,d[n+i*n]);
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值