【图论经典题目讲解】洛谷 P5304 旅行者

P5304 旅行者

D e s c r i p t i o n \mathrm{Description} Description

给定一个 n n n 个点, m m m 条边的有向图,求解 k k k 个点两两间最短路长度的最小值。

S o l u t i o n \mathrm{Solution} Solution

对于 k k k 个点,可以考虑二进制分组优化,即对于每一位为 1 1 1 的点放入 1 1 1 组(设为 A A A 组),为 0 0 0 的点放入 1 1 1 组(设为 B B B 组)。

则如果建立一个虚拟源点和一个虚拟汇点,并由虚拟源点向 A A A 组中的每一个点连一条长度为 0 0 0 的边,且由 B B B 组中的每一个点向虚拟汇点连一条长度为 0 0 0 的边,那么虚拟源点到虚拟汇点的最短路即为 A A A 组中任意一个点到 B B B 组中任意一个点的最短路长度的最小值。

正确性证明:对于 k k k 个点中的任意 2 2 2 个点,由于编号是不同的,那么必定有一位二进制是不同的,所以总有一次一个点在 A A A 组,一个点在 B B B 组,这 2 2 2 个点之间的最短路也一定会给答案贡献 1 1 1 次。

C o d e Code Code

cpp 复制代码
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int SIZE = 4e7 + 10, SIZE2 = 1e5 + 10;

int N, M, K;
int h[SIZE2], hs[SIZE2], e[SIZE], ne[SIZE], w[SIZE], idx;
int A[SIZE2], Dist[SIZE2], Vis[SIZE2];

void add(int h[], int a, int b, int c)
{
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

int Dijkstra(int S, int T)
{
	memset(Dist, 0x3f, sizeof Dist);
	memset(Vis, 0, sizeof Vis);
	priority_queue<PII, vector<PII>, greater<PII>> Heap;

	Heap.push({0, S}), Dist[S] = 0;
	while (Heap.size())
	{
		auto Tmp = Heap.top();
		Heap.pop();

		int u = Tmp.second;
		if (Vis[u]) continue;
		Vis[u] = 1;

		for (int i = hs[u]; ~i; i = ne[i])
		{
			int j = e[i];
			if (Dist[j] > Dist[u] + w[i])
			{
				Dist[j] = Dist[u] + w[i];
				Heap.push({Dist[j], j});
			}
		}
	}

	return Dist[T];
}

void solve()
{
	memset(h, -1, sizeof h);
	memset(hs, -1, sizeof hs);

	cin >> N >> M >> K;

	int u, v, c;
	while (M --)
	{
		cin >> u >> v >> c;
		add(h, u, v, c);
	}

	for (int i = 1; i <= K; i ++)
		cin >> A[i];

	memcpy(hs, h, sizeof h);

	int S = 0, T = N + 1, Result = 1e18;
	for (int i = 0; i < 17; i ++)
	{
		for (int j = 1; j <= K; j ++)
			if (A[j] >> i & 1)
				add(hs, S, A[j], 0);
			else
				add(hs, A[j], T, 0);
		Result = min(Result, Dijkstra(S, T));
		memcpy(hs, h, sizeof h);
		for (int j = 1; j <= K; j ++)
			if (A[j] >> i & 1)
				add(hs, A[j], T, 0);
			else
				add(hs, S, A[j], 0);
		Result = min(Result, Dijkstra(S, T));
		memcpy(hs, h, sizeof h);
	}

	cout << Result << endl;
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int Data;

	cin >> Data;

	while (Data --)
		solve();

	return 0;
}
相关推荐
OCR_133716212753 分钟前
护照OCR校验位技术解析:从算法逻辑到工程落地,筑牢证件核验安全线
人工智能·算法
Hello.Reader4 分钟前
算法基础(十三)——随机算法为什么有时主动引入随机性
java·数据库·算法
老鱼说AI22 分钟前
现代 LangChain 开发指南:从 LCEL 原理到企业级 RAG 与 Agent 实战
java·开发语言·人工智能·深度学习·神经网络·算法·机器学习
小许同学记录成长42 分钟前
基于幅度形态与参数聚类的工作模式判别
python·算法·scikit-learn
郝学胜-神的一滴43 分钟前
Qt 入门 01-02: 开发环境搭建指南
开发语言·c++·qt·客户端
Languorous.1 小时前
C++数据结构高阶|布隆过滤器(Bloom Filter)深度解析:从原理到手写实现,面试高频考点全覆盖
数据结构·c++·面试
gumichef1 小时前
二叉树_堆
算法
Liangwei Lin1 小时前
LeetCode 70. 爬楼梯
算法
山河木马1 小时前
Emscripten 从 C/C++ 调用 JavaScript
前端·javascript·c++
TANGLONG2221 小时前
【C++】继承详解——基类/派生类、作用域、默认函数、菱形继承(超详细)
java·c语言·c++·经验分享·笔记·ajax