【2022天梯赛训练】3月20日线上测试赛 L3-3 xt的考研路 (30 分) 题解

原题链接:L3-3 xt的考研路 (30 分)

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

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

子图的定义:

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

输入格式:

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

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

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

from to w

0 <= from, to <= n - 1,from != to,1 <= w <= 10^9

输出格式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 的路径,所以不存在任何子图满足所有限制。

题意:

给出一有向图,再给出起点1起点2和终点,需要选出一子图,使得两个起点都能到达终点,并且是的子图内的所有边权之和最小

标签:

Dijkstra

分析:

       首先不要混淆了子图和导出子图的概念,导出子图更加严格,在导出子图的概念下,假设选出的点在一个集合内,那么集合内所有点连成的边都需要被选中。而子图的概念并没有此项要求,对于边的要求是任意的,只需要在原来的边集集合中即可。换句话来说,题目要使得选出的子图边权之和最小,如果我在构造两条最短路时并没有用到某条边,那我完全可以不用选上这条边

       现在我们只需要单纯地考虑两条最短路径了,即起点1到终点,起点2到终点。但是最终的答案可能并不是两条最短路简单相加。因为可能两条最短路会有部分或者完全重合(即使没有重合,我们也可以看成是在终点重合,所有两条最短路一定会重合)。而且我们为了使得最后的答案最小,我们需要使得重合的部分尽量多,所以说直接跑两遍最短路再减去重复部分可能得不到最优解,因为二者的目的不同。

      我们可以想象一下,最终的子图会是一个什么形状。应该是一个“Y”字型,即两个起点到达某一个中间点,再一起从中间点到终点。即便是两条路径完全重合或者完全不重合,我们也可以将它特殊地看成是中间点就是某一个起点或者终点。

      那么我们只需要枚举中间点,那么枚举的前提是什么呢,我们需要通过Dijkstra预处理出来从起点1,起点2出发的单源最短路径的dist1,dist2数组,同时还需要建立反向边,从终点出发的单源最短路径的dist3数组。随后枚举中间点取最小值即可。

      注意数据范围,点和边都在10^5级别,只能用堆优化版Dijkstra,同时最后的答案可能会爆int,需要开long long。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>

using namespace std;

int n, m;
int src1, src2, dest;
const long MAX = 0x3f3f3f3f;

vector<long> dijkstra(vector<vector<pair<int, int>>> &g, int start) {
    vector<long> dis(g.size(), MAX / 3);
    dis[start] = 0;
    priority_queue<pair<long, int>, vector<pair<long, int>>, greater<pair<long, int>>> pq;
    pq.emplace(0, start);
    while (!pq.empty()) {
        pair<long, int> t = pq.top();
        pq.pop();
        
        long d = t.first;
        int x = t.second;
        if (d > dis[x]) continue;
        for (auto t: g[x]) {
        	long y = t.first;
        	int wt = t.second;
            long newD = dis[x] + wt;
            if (newD < dis[y]) {
                dis[y] = newD;
                pq.emplace(newD, y);
            }
        }
    }
    return dis;
}

int main()
{
	//freopen("data.txt", "r", stdin);
	cin >> n >> m;
	cin >> src1 >> src2 >> dest;
	vector<vector<pair<int, int>>> g(n), rg(n);
	while(m--) {
		int x, y, wt;
		cin >> x >> y >> wt;
		g[x].emplace_back(y, wt);
        rg[y].emplace_back(x, wt);
	}

    auto d1 = dijkstra(g, src1);
    auto d2 = dijkstra(g, src2);
    auto d3 = dijkstra(rg, dest);

    long ans = MAX / 3;
    for (int x = 0; x < n; ++x)
        ans = min(ans, d1[x] + d2[x] + d3[x]);
    
    if(ans < MAX / 3) {
    	cout << ans << endl;
	} else {
		cout << "xt,我好没本领!" << endl;
	}
    return 0;	
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值