Deliver the Cake(四,学习总结)

本文介绍了一种改进的Dijkstra算法,用于解决包含特殊类型节点(M、L、R)的最短路径问题。通过拆分M类型节点,转化为标准图论问题,再运用Dijkstra算法求解。文章提供了详细的代码实现和解析。

在这里插入图片描述
题意:有m个点和n条路,点分为M,L,R三种类型,从L到R或者R到L都会花多于的时间x,而M不会,求从点s到点t花的最少时间。
思路:因为有M这种类型阻碍了直接使用Dijkstra算法,但可将M分为L和R两个点来解决,这样就构成一个新的图,利用Dijkstra算法来解决即可。
www.manongjc.com/detail/18-iowrqwislapwaoq.html
学习的是该链接中的代码,比起发的题解自己更容易理解些。
注意:要理解Dijkstra算法和点是怎样拆分的。

#include <bits/stdc++.h>
#include <stack>
#include <map>
#include <queue>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
typedef long long ll;
using namespace std;
const int maxn = 2e5+10; 
const int INF = 0x3f3f3f3f; 
ll tcase,n,m,s,t,x,v[maxn];
char f[maxn];
struct shudui{
	ll start,val;//起点和值 
	bool operator < (const shudui y) const{
		return val > y.val;
	}
}str1,str2; 
priority_queue<shudui>r;//优先队列 
vector<shudui>w[maxn];//变长数组 
void add_edge(ll a,ll b,ll c)//连边
{
	str2.start = b;
	str2.val = c;
	w[a].push_back(str2);
	str2.start = a;
	w[b].push_back(str2);
 } 
void JK(ll st){
	memset(v,INF,sizeof(v));
	v[st] = 0;
	str1.start = st;
	str1.val = 0;
	r.push(str1);
	while(!r.empty()){
		ll x,y;
		str1 = r.top();
		r.pop();
		x = str1.start;
		y = str1.val;
		if(v[x]<y) continue;
		ll len = w[x].size();
		for(ll i=0;i<len;i++){
			str2 = w[x][i];
			if(v[x]+str2.val<v[str2.start]){
				v[str2.start]=v[x]+str2.val;
				str1.start = str2.start;
				str1.val = v[str2.start];
				r.push(str1);
			}
		}
	}
}
int main(){
    scanf("%lld",&tcase);
	while(tcase--){
		scanf("%lld%lld%lld%lld%lld",&n,&m,&s,&t,&x);
		for(ll i=0;i<=2*n;i++){
			w[i].clear();//用前清空 
		} 
		scanf("%s",f+1); //输入路的类型 
		while(m--){
	     ll a,b,c;
		 scanf("%lld%lld%lld",&a,&b,&c);//输入起点,终点和长度
		 //将M类型的路分成两种L和R,然后分情况讨论 
		 //①都为L或R
		 if(f[a]==f[b]&&(f[a]=='L'||f[b]=='R')){
		 	add_edge(a,b,c);//为原长		
		}
		//②一个为R,一个为L 
		else if(f[a]!=f[b]&&f[a]!='M'&&f[b]!='M'){
			add_edge(a,b,c+x);//switch一次,长度为c+x 
		}
		//③起点为L,终点为M,将M分为L和R 
		else if(f[a]=='L'&&f[b]=='M'){
			add_edge(a,b,c);//当M为L
			add_edge(a,b+n,c+x);//当M为R 
		} 
		//④起点为M,终点为L
	   else if(f[a]=='M'&&f[b]=='L'){
	   	add_edge(a,b,c);//当M为L
		   add_edge(a+n,b,c+x);//当M为R 
	   } 
	   //⑤起点为R,终点为M
	   else if(f[a]=='R'&&f[b]=='M'){
	   	add_edge(a,b,c+x);//当M为L
		   add_edge(a,b+n,c);//当M为R 
	   }
	   //⑥起点为M,终点为R
	   else if(f[a]=='M'&&f[b]=='R'){
	   	add_edge(a+n,b,c);//当M为R
		   add_edge(a,b,c+x);//当M为L 
	   }
	   //当都为M,有四种走法 
	   else {
	   	add_edge(a,b,c);//M都为L 
	   	add_edge(a,b+n,c+x);//起点为L,终点为R 
	   	add_edge(a+n,b,c+x);//起点为R,终点为L 
	   	add_edge(a+n,b+n,c);//都为R 
	   }
	 } 
	 ll ans = 0;
	 if(f[s]=='M'){//s为起点 
	 	add_edge(0,s,0);//以L为起点
		 add_edge(0,s+n,0);//以M为起点
		 JK(0);
		 ans = min(v[t],v[t+n]); 
	 } 
	 else{
	 	JK(s);
	 	ans = min(v[t],v[t+n]);
	 }
	 printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值