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;
}