【分层图 最短路 迪氏堆优化最短路】B4165 [BCSP-X 2024 12 月初中组] 贸易|普及+
本文涉及知识点
C++图论 分层图 最短路
B4165 [BCSP-X 2024 12 月初中组] 贸易
题目描述
这个世界上一共有 n n n 个国家,这些国家之间经常有贸易往来,于是为了方便,有 m m m 条道路连接这些国家,每条道路连接两个国家,使得这两个国家之间可以轻松进行往来。
有了这些道路之后,商人在国家之间会赚取到更多的利润,所以为了限制商人的财富,国家之间制定了一个规则。商人经过每条道路,需要上交这条路对应的过路费 w i w_i wi,商人从起点国家到达目的地国家时,会返还给他走的路径上的过路费最大的那条路的费用 max w i \max w_i maxwi 减去过路费最小的那条路的费用 min w i \min w_i minwi。
现在,有 k k k 个商人要从一号国家出发,去各个国家进行贸易,你需要计算他们每个人如何走可以使得他自己的过路费最少,你只需要告诉他们每个人这个最小过路费即可。
输入格式
第一行三个整数 n , m , k n, m, k n,m,k,分别表示国家的个数,道路的数量和商人的数量。国家的编号由 1 到 n n n。
接下来 m m m 行每行三个整数 u i , v i , w i u_i, v_i, w_i ui,vi,wi,表示有一条连接 u i u_i ui 号国家和 v i v_i vi 号国家的道路,其过路费为 w i w_i wi。
接下来 k k k 行每行一个整数 t i t_i ti,表示每个商人的目的地国家编号。
输出格式
输出共 k k k 行,一行一个整数 c i c_i ci,表示第 i i i 名商人要到达目的地所需要的最小花费。
输入输出样例 #1
输入 #1
5 4 4
5 3 4
2 1 1
3 2 2
2 4 2
2
3
4
5
输出 #1
1
2
2
4
输入输出样例 #2
输入 #2
6 8 5
3 1 1
3 6 2
5 4 2
4 2 2
6 1 1
5 2 1
3 2 3
1 5 4
2
3
4
5
6
输出 #2
2
1
4
3
1
说明/提示
样例解释 1
如上图。
- 对于路径 1 → 2 1 \to 2 1→2,花费为 1 − 1 + 1 = 1 1 - 1 + 1 = 1 1−1+1=1;
- 对于路径 1 → 3 1 \to 3 1→3,花费为 1 + 2 − 2 + 1 = 2 1 + 2 - 2 + 1 = 2 1+2−2+1=2;
- 对于路径 1 → 4 1 \to 4 1→4,花费为 1 + 2 − 2 + 1 = 2 1 + 2 - 2 + 1 = 2 1+2−2+1=2;
- 对于路径 1 → 5 1 \to 5 1→5,花费为 1 + 2 + 4 − 4 + 1 = 4 1 + 2 + 4 - 4 + 1 = 4 1+2+4−4+1=4;
数据范围
- 对于 10 % 10\% 10% 的数据, n ≤ 10 n \leq 10 n≤10;
- 对于 30 % 30\% 30% 的数据, n ≤ 2 × 10 3 n \leq 2 \times 10^3 n≤2×103;
- 对于另外 20 % 20\% 20% 的数据, k = 1 k = 1 k=1;
- 对于另外 10 % 10\% 10% 的数据, w i w_i wi 相同;
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 10 5 , n − 1 ≤ m ≤ min ( n ( n − 1 ) 2 , 2 × 10 5 ) , 1 ≤ k ≤ n − 1 , 0 ≤ w i ≤ 10 9 1 \leq n \leq 2 \times 10^5, n - 1 \leq m \leq \min(\frac{n(n - 1)}{2}, 2 \times 10^5), 1 \leq k \leq n - 1, 0 \leq w_i \leq 10^9 1≤n≤2×105,n−1≤m≤min(2n(n−1),2×105),1≤k≤n−1,0≤wi≤109,数据保证不存在重边和自环。
分层图 最短路 迪氏堆优化最短路
如果路径只有一条边,无减免。
如果路径有两条或更多的边。 ⟺ \iff ⟺ 最长的边的权重为0, 最短的边权重乘以2的最短路 ⟺ \iff ⟺ 任意边权重0,另外任意一边权重乘以2。
4层分层图,层内无向边,各层都等于原始图。
层间图都是有向图,跨层路径有三条:
一,0层到3层,权重和原始图相同。对应一条边的路径。注意:没有3层到0层的边。
二,0层到1层,1层到3层。前者权重0,后者权重乘以2。对应,先0边,后2边。
三,0层到2层,2层到3层。前者权重乘以2,后者权重0。对应,先2边,后0边。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5, class T6, class T7 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) >> get<6>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};typedef pair<long long, int> PAIRLLI;
class CHeapDis
{
public:CHeapDis(int n, long long llEmpty = LLONG_MAX / 10) :m_llEmpty(llEmpty){m_vDis.assign(n, m_llEmpty);}void Cal(int start, const vector<vector<pair<int, int>>>& vNeiB){std::priority_queue<PAIRLLI, vector<PAIRLLI>, greater<PAIRLLI>> minHeap;minHeap.emplace(0, start);while (minHeap.size()){const long long llDist = minHeap.top().first;const int iCur = minHeap.top().second;minHeap.pop();if (m_llEmpty != m_vDis[iCur]){continue;}m_vDis[iCur] = llDist;for (const auto& it : vNeiB[iCur]){minHeap.emplace(llDist + it.second, it.first);}}}vector<long long> m_vDis;const long long m_llEmpty;
};
class Solution {
public:vector<long long> Ans(const int N, vector<tuple<int, int, int>>& edge, vector<int>& dest) {vector<vector<pair<int, int>>> neiBo(4 * N);CHeapDis dis(4 * N);for (auto [u, v, w] : edge) {u--, v--;//层次for (int i = 0; i < 4; i++) {neiBo[u + N * i].emplace_back(v + N * i, w);neiBo[v + N * i].emplace_back(u + N * i, w);}//0层到3层的图neiBo[u + N * 0].emplace_back(v + N * 3, w);neiBo[v + N * 0].emplace_back(u + N * 3, w);//0层到1层,1层到3层neiBo[u + N * 0].emplace_back(v + N * 1, 0);neiBo[v + N * 0].emplace_back(u + N * 1, 0);neiBo[u + N * 1].emplace_back(v + N * 3, w * 2);neiBo[v + N * 1].emplace_back(u + N * 3, w * 2);//0层到2层,2层到3层neiBo[u + N * 0].emplace_back(v + N * 2, w * 2);neiBo[v + N * 0].emplace_back(u + N * 2, w * 2);neiBo[u + N * 2].emplace_back(v + N * 3, 0);neiBo[v + N * 2].emplace_back(u + N * 3, 0);}dis.Cal(0, neiBo);vector<long long> ans;for (const auto& d : dest) {ans.emplace_back(dis.m_vDis[N * 3 + d - 1]);}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; int N, M, Q;cin >> N >> M >> Q;auto edge = Read<tuple<int, int, int>>(M);auto dest = Read<int>(Q);
#ifdef _DEBUG printf("N=%d",N);Out(edge, ",edge=");Out(dest, ",dest=");//Out(edge, ",edge="); /*Out(que, ",que=");*///Out(ab, ",ab=");//Out(par, "par=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(N,edge,dest);for (const auto& i : res){cout << i << "\n";}return 0;
};
单元测试
int N;vector<tuple<int, int, int>> edge;vector<int> dest;TEST_METHOD(TestMethod11){N = 5, edge = { {5,3,4},{2,1,1},{3,2,2},{2,4,2} }, dest = { 2,3,4,5 }; auto res = Solution().Ans(N, edge, dest);AssertV({ 1,2,2,4 }, res);}TEST_METHOD(TestMethod12){N = 6, edge = { {3,1,1},{3,6,2},{5,4,2},{4,2,2},{6,1,1},{5,2,1},{3,2,3},{1,5,4} }, dest ={ 2,3,4,5,6 };auto res = Solution().Ans(N, edge, dest);AssertV({2,1,4,3,1 }, res);}
# 扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。