Free restricted flights (拆边建图)

本文介绍了一种解决两人旅行路径规划问题的算法。该问题涉及如何利用有限数量的免费机票找到一条从各自起点出发,在某地会合后再返回起点的最优路径。通过拆边技巧和双向迪杰斯特拉算法实现解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
这道题讲的是两个人去旅行,然后他们会在一个地方(不是两者的起点)碰头,并且最后还要回到各自的起点,然后有 k k k 张免费的机票,求总的价值以及他们可能会碰头的其中一个点。

由于有 k k k 张免费的机票,并且难以模拟在哪一趟兑换这张免费机票,所以采用拆边的做法,将一段路拆分成 k + 1 k+1 k+1 段,第一段表示需要付费的票,后面分别表示使用第一张免费票的路线,第二张…第 k k k

这里后面回到起点的做法,直接在原来给出的建图的基础上建立反边即可,便把起点当作源点,按部就班的跑一遍 d i j k s t r a dijkstra dijkstra 即可

此题还有个坑点就是,在路线较少的时候, k k k 张免费的机票可能用不完,需要去对这 k k k 张票就是枚举

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void rd(T& x)
{
	ll tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e4 + 1;
const int M = 1e6 + 10;
const double eps = 1e-8;
struct edge {
	int next, to, w;
}e1[M], e2[M];
int head1[N*11], head2[N*11], cnt1, cnt2;
void add(edge e[], int head[], int& cnt, int u, int v, int w) {
	e[cnt].to = v;
	e[cnt].next = head[u];
	e[cnt].w = w;
	head[u] = cnt++;
}
int n, m, a, b, k;
int id[N][11];
ll dis1[N*11], dis2[N*11], dis3[N*11], dis4[N*11];
bool vis[N*11];
struct node {
	int v;
	ll len;
	bool friend operator<(const node& a, const node& b) {
		return a.len > b.len;
	}
	node(int v, ll len) :v(v), len(len) {}
};
void dijkstra(edge e[], int head[], int& cnt, ll dis[], int st) {
	for (int i = 0; i < N * 11; ++i) dis[i] = 1e16;
	dis[st] = 0;
	memset(vis, false, sizeof vis);
	priority_queue<node>que;
	que.push(node(st, 0));
	while (!que.empty()) {
		node vn = que.top(); que.pop();
		if (vis[vn.v]) continue;
		int u = vn.v;
		vis[u] = true;
		dis[u] = vn.len;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (vis[v]) continue;
			que.push(node(v, vn.len + e[i].w));
		}
	}
}
int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
//	FILE* _INPUT = freopen("input.txt", "r", stdin);
//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
	memset(head1, -1, sizeof head1); memset(head2, -1, sizeof head2); cnt1 = cnt2 = 0;
	rd(n), rd(m), rd(a), rd(b), rd(k);
	int num = 0;
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j <= k; ++j) {
			id[i][j] = ++num;
		}
	}
	while (m--) {
		int u, v, w; rd(u), rd(v),rd(w);
		for (int j = 0; j < k; ++j) {
			add(e1, head1, cnt1, id[u][j], id[v][j], w);
			add(e1, head1, cnt1, id[u][j], id[v][j + 1], 0);
			add(e2, head2, cnt2, id[v][j], id[u][j], w);
			add(e2, head2, cnt2, id[v][j], id[u][j + 1], 0);
		}
		add(e1, head1, cnt1, id[u][k], id[v][k], w);
		add(e2, head2, cnt2, id[v][k], id[u][k], w);
	}
	dijkstra(e1, head1, cnt1, dis1, id[a][0]);
	dijkstra(e1, head1, cnt1, dis2, id[b][0]);
	dijkstra(e2, head2, cnt2, dis3, id[a][0]);
	dijkstra(e2, head2, cnt2, dis4, id[b][0]);
	int pos = -1; ll ans = 1e16;
	for (int i = 0; i < n; ++i) {
		if (a == i || b == i) continue;
		ll ans1 = 1e16, ans2 = 1e16;
		for (int j1 = 0; j1 <= k; ++j1) {
			for (int j2 = 0; j2 <= k - j1; ++j2) {
				int v1 = id[i][j1];
				int v2 = id[i][j2];
				if (ans1 > dis1[v1] + dis3[v2]) {
					ans1 = dis1[v1] + dis3[v2];
				}
				if (ans2 > dis2[v1] + dis4[v2]) {
					ans2 = dis2[v1] + dis4[v2];
				}
			}
		}
		if (ans > ans1 + ans2) {
			ans = ans1 + ans2;
			pos = i;
		}
	}
	if (ans == 1e16) puts(">:(");
	else printf("%d %lld\n", pos, ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值