分层图最短路

本文讨论了一种在有限制条件下求解分层最短路径问题的方法,通过拆点和状态表示,利用优先队列实现动态规划,同时考虑使用和不使用免费机会的策略。
摘要由CSDN通过智能技术生成

分层最短路用更加具体或者形象一点的说法就是有限制的最短路径问题。
通常是拆点解决问题,原图中的点加上一个当前点的状态,成为一个新的点

P4568 [JLOI2011] 飞行路线

P4568 [JLOI2011] 飞行路线

#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define inf 0x3f3f3f3f

using namespace std;

void solve()
{
	int n,m,k;
	cin>>n>>m>>k;
	int s,t;
	cin>>s>>t;
	vector<vector<pii>>g(n+1);
	rep(i,1,m){
		int u,v,w;
		cin>>u>>v>>w;
		//无向图,建双向边
		g[u].pb({v,w});
		g[v].pb({u,w});
	}
	struct node{
		int val,u,cnt;
		//大根堆只能重载小于号,小根堆只能重载大于号
		bool operator<(const node&t)const{
			return val>t.val;
		}
	};
	priority_queue<node>q;
	vector<vector<int>>d(n+1,vector<int>(k+1,0x3f3f3f3f));
	vector<vector<int>>vis(n+1,vector<int>(k+1));
	d[s][0]=0;
	q.push({0,s,0});
	while(q.size()){
		auto t=q.top();
		q.pop();
		int val=t.val,u=t.u,cnt=t.cnt;
		if(vis[u][cnt])	continue;
		vis[u][cnt]=1;
		for(auto it:g[u]){
			int v=it.x,w=it.y;
			//不用免费的机会
			if(d[v][cnt]>d[u][cnt]+w){
				d[v][cnt]=d[u][cnt]+w;
				q.push({d[v][cnt],v,cnt});
			}
			//用免费的机会
			if(cnt<k&&d[v][cnt+1]>d[u][cnt]){
				d[v][cnt+1]=d[u][cnt];
				q.push({d[v][cnt+1],v,cnt+1});
			}
		}
	}
	int res=0x3f3f3f3f*1ll;
	rep(i,0,k){
		res=min(res,d[t][i]);
	}
	cout<<res<<endl;
}

signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}


在这里插入图片描述
在这里插入图片描述

图论真难qwq
这道题目也可以用分层图的思想,或者dp的思想去解决
分层图并不一定要把图建出来,只需要进行拆点。
对于这类有选择会导致不同结果的可以通过拆点来解决
把原图上的点加上到达该点时的状态结合产生新的点。
这道题目和最短路的转移也不一样了,与其说是最短路但是感觉更像是用队列去写的dp,因为题目中并没有说是 D A G DAG DAG
d [ u ] [ c n t ] : 表示到达 u 点前面已经用过 c n t 次的最大边权 d[u][cnt]:表示到达u点前面已经用过cnt次的最大边权 d[u][cnt]:表示到达u点前面已经用过cnt次的最大边权
转移:考虑使用和不使用
不使用

//不用这次免费的边
if(max(d[u][cnt],w)<d[v][cnt]){
	d[v][cnt]=max(d[u][cnt],w);
	if(!inq[v][cnt]){
		q.push({v,cnt});
		inq[v][cnt]=1;
}

使用:需要还有次数才能使用

//使用
if(cnt<k&&d[u][cnt]<d[v][cnt+1]){
	d[v][cnt+1]=d[u][cnt];
	if(!inq[v][cnt+1]){
		q.push({v,cnt+1});
		inq[v][cnt+1]=1;
	}
}
#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define inf 0x3f3f3f3f*1ll

using namespace std;

void solve()
{
	int n,m,k;
	cin>>n>>m>>k;
	
	vector<vector<pii>>g(n+1);
	rep(i,1,m){
		int u,v,w;
		cin>>u>>v>>w;
		g[u].pb({v,w});
		g[v].pb({u,w});
	}
	
	vector<vector<int>>inq(n+1,vector<int>(k+1,0));
	vector<vector<int>>d(n+1,vector<int>(k+1,0x3f3f3f3f));
	
	queue<pii>q;
	d[1][0]=0;
	inq[1][0]=1;
	q.push({1,0});
	//跑一遍最短路
	while(q.size()){
		auto t=q.front();
		q.pop();
		int u=t.x,cnt=t.y;
		inq[u][cnt]=0;
		for(auto it:g[u]){
			int v=it.x,w=it.y;
			//不用这次免费的边
			if(max(d[u][cnt],w)<d[v][cnt]){
				d[v][cnt]=max(d[u][cnt],w);
				if(!inq[v][cnt]){
					q.push({v,cnt});
					inq[v][cnt]=1;
				}
			}
			//使用
			if(cnt<k&&d[u][cnt]<d[v][cnt+1]){
				d[v][cnt+1]=d[u][cnt];
				if(!inq[v][cnt+1]){
					q.push({v,cnt+1});
					inq[v][cnt+1]=1;
				}
			}
		}
	}
	int res=inf;
	rep(i,0,k){
		res=min(res,d[n][i]);
	}
	
	cout<<(res==inf?-1:res)<<endl;
}

signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值