PTA xt的考研路 (迪杰斯特拉+堆优化+链式前向星)

xt是我院19级专业第一,但他认为保研并不能展示他全部的实力,所以他在22年初试一结束就加入了23考研的队伍中,并且他为了填补我院近些年来无北大研究生的空白,毅然决然决定扛起19级的大旗,在学校百年华诞之际献上他最诚挚的礼物。

xt每天都游走在寝室,食堂和图书馆,三点一线,即便是在疫情局势蔓延的形势下,凌晨三点半刚做完核酸,他六点半还是照常起来卷。现在他太忙了,好像在提前准备复试了,想让你帮个小忙,xt会给出学校的地图(有向图),并且给出寝室,食堂和图书馆的编号(编号从0开始),希望你从该图中找出一个子图,要使得在这个子图中,寝室能够到达图书馆,食堂也能到达图书馆,同时希望在这个子图中的所有边的边权之和最小。如果你找不到任何一个子图符合要求的话(),输出“xt,我好没本领!”,因为你找不到并不代表xt找不到

子图的定义:

从原图中删去一些点或删去一些线或既删去一些点又删去一些线,剩下的部分(当然必须仍然是图)。允许两种极端情况:什么都不删;删去所有点和所有线。

输入格式:

第一行输入点的个数 n,3 <= n <= 105,边的个数 m,0 <= m <= 2∗105

第二行给出寝室的编号id1,食堂的编号id2,图书馆的编号id3,题目保证三个编号两两不同。

随后 m 行按照以下形式描述边,表示有一条有向边,起点是from,终点是to,权值是w

from to w

0 <= from, to <= n - 1,from != to,1 <= w <= 109

输出格式1:

如果子图存在则输出最小边权和,如果不存在输出“xt,我好没本领!”

输入样例1:

6 9
0 1 5
0 2 2 
0 5 6 
1 0 3 
1 4 5 
2 1 1 
2 3 3 
2 3 4 
3 4 2 
4 5 1

输出样例1:

9

解释:

上图为输入的图。

蓝色边为最优子图之一。

注意,直接选择0 1 5三点构成的子图也能得到最优解,但无法在满足所有限制的前提下,得到更优解。

输入样例2:

3 2
0 1 2
0 1 1
2 1 1

输出样例2:

xt,我好没本领!

解释:

上图为输入的图。

可以看到,不存在从节点 1 到节点 2 的路径,所以不存在任何子图满足所有限制。

思路:连接三个点共用的路越多,权值越小,不管怎么变化,最终的子图都会变成倒过来“Y”形状

所以分别对寝室,食堂,图书馆 跑一遍最短路,然后枚举中间点(中间点可以是寝室,食堂,图书馆 ) 分别对应了极端情况。

取出答案即可,注意从图书馆往回跑要存反图。

AC代码:

#include<bits/stdc++.h>
using namespace std;
long long head[100086],head_f[100086],dis1[100086],dis2[100086],dis3[100086];
long long Max = 0x3f3f3f3f;//最大值 
int cnt =0;//编号 
struct TT{//结构体存边 
	int next;
	int to;
	int val;
}edge[5*100086],edge_f[5*100086];//题目给的是2*1e5 但是我正边和反边共用了一个cnt导致容量是他俩相加 
struct T{//堆优化 
	int id;
	long long c;
	bool operator < (const T &x)const{//优先队列重载排序 
		return c > x.c;//重载的大小是相反的 
	}
};
void add(int u,int v,int c,long long head[],TT edge[]){//加边函数,通过传入相关数组实现一个函数就可以存正边和反边,减少代码量 
	cnt ++;
	edge[cnt].to = v;//存目的地 
	edge[cnt].val  =c;//存权值 
	edge[cnt].next = head[u];//记录旧头 
	head[u]=cnt;//更新头
	return ;
}
void dij(int x,long long head[],TT edge[],long long dis[]){//通过传入相关数组,实现一个dij函数跑出的结果分别存储在不同数组中 
	priority_queue<T>q;//堆优化 
	q.push({x,0});
	dis[x]=0;//自己到自己是0; 
	while(!q.empty()){
		int now = q.top().id;
		q.pop();
		for(int u = head[now];u!=-1;u = edge[u].next){
			int v = edge[u].to;
			if(dis[v] > edge[u].val + dis[now]){
				dis[v] = edge[u].val + dis[now];
				q.push({v,dis[v]});
			}
		}
		
	}
	return ;
}
int main(){
	int n,m;
	cin>>n>>m;
	int qs,st,tsg;
	cin>>qs>>st>>tsg;
	memset(head,-1,sizeof head);//初始化,千万别忘记把头初始化成-1 
	memset(head_f,-1,sizeof head_f);
	memset(dis1,Max/3,sizeof dis1);//初始化距离 ,因为ans是dis1+dis2+dis3,3个long long 相加会爆long long  
	memset(dis2,Max/3,sizeof dis2);//所以除以三,防止爆long long ; 
	memset(dis3,Max/3,sizeof dis3);
	for(int i=0;i<m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c,head,edge);//正边 
		add(b,a,c,head_f,edge_f);//反边 
	}
	dij(qs,head,edge,dis1);//从寝室到其他点的距离,存储到dis1 
	dij(st,head,edge,dis2);//从食堂到其他点的距离,存储到dis2
	dij(tsg,head_f,edge_f,dis3);//从图书馆到其他点的距离,存储到dis3,注意是反边。 
	long long ans =Max;
	for(int i=0;i<n;i++){
		if(dis1[i]==0){
		}
		ans = min(ans,dis1[i]+dis2[i]+dis3[i]);//取答案 
	}
	if(ans < Max/3){
		cout<<ans<<endl;
	}else{
		cout<<"xt,我好没本领!"<<endl;
	}
	return 0;
} 

2024/03/24 更新版

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100086;
const int MAX = 0x3f3f3f3f;
struct TT{
	int to,next,val;
}edges[N*5];
struct T{
	int id,val;
	bool operator < (const T& x)const{ return val > x.val; } 
};
int cnt = 0,head[N],head_r[N],dis1[N],dis2[N],dis3[N],n,m,qs,st,tsg;
void add(int u,int v,int w,int hd[]){
	edges[cnt].to = v;
	edges[cnt].val = w;
	edges[cnt].next = hd[u];
	hd[u] = cnt++;
}
void dij(int st,int hd[],int dis[]){
	priority_queue<T>q;
	q.push({st,0});
	dis[st]  = 0;
	while(!q.empty()){
		int now = q.top().id;q.pop();
		for(int i=hd[now];i!=-1;i=edges[i].next){
			int v = edges[i].to;
			int w = edges[i].val;
			if(dis[v] > dis[now]+ w){
				dis[v] = dis[now]+ w;
				q.push({v,dis[v]});
			}
		}
	}
}
signed main(){
	cin>>n>>m>>qs>>st>>tsg;
	memset(head,-1,sizeof head);
	memset(head_r,-1,sizeof head_r);
	memset(dis1,MAX/3,sizeof dis1);
	memset(dis2,MAX/3,sizeof dis2);
	memset(dis3,MAX/3,sizeof dis3);
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w,head);
		add(v,u,w,head_r);
	}
	dij(qs,head,dis1);
	dij(st,head,dis2);
	dij(tsg,head_r,dis3);
	int ans = LONG_LONG_MAX;
	for(int i=0;i<n;i++){
		ans = min(ans , dis1[i]+dis2[i]+dis3[i]);
	}
	if(ans < MAX / 3){
		cout<<ans<<endl;
	}else{
		cout<<"xt,我好没本领!"<<endl;
	}
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值