CCF 行车路线(多维最短路,两种类型的道路)

本文介绍了CCF行车路线问题,重点讨论了处理多维最短路径时涉及的两种类型道路——小道和大道。通过Floyd算法预处理各点间小道距离,状态包括从小道到达和从大道到达。转移策略包括大道到大道、大道到小道,但不允许小道到小道直接连接,以避免连续走两条小道的错误路径。

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

题解

先用Floyd预处理各个点小道的距离,每个点有两个状态:从小道到达d1[u]和从大道到达d2[u]。转移方式为:
d1[u]:d2[v]+w; 大道到达的点+小道
d2[u]: d2[u]+w; 大道到达的点+大道
d2[u]: d1[v]+w; 小道到达的点+大道

为什么小道的点不能从小道到达的点+小道呢?
因为我们已经预处理过了,比如u->v->w,其中(u,v),(v,w)都是小道,u是大道到达的点,d[w] = d[v]+w;的话是不正确的,因为连续走了两条小道。所以,小道到达的点一定是从一个大道到达的点走一连串小道到达的。

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 500+5;
const ll INF = 1e18;
int n,m;
ll g1[maxn][maxn],g2[maxn][maxn];
ll d1[maxn],d2[maxn];
int cnt[maxn], inq[maxn];
void spfa() {
	// memset(d1,INF,sizeof d1);
	// memset(d2,INF,sizeof d2);
	d1[1] = 0;
	d2[1] = 0;
	queue<int>que;
	que.push(1);
	inq[1] = true;
	// cnt[1] = 1;
	while(!que.empty()) {
		int u = que.front();
		que.pop();
		// cout << u << " " << d2[u]<<" "<<d1[u] << endl;
		inq[u] = false;
		for(int i = 1; i <= n; ++i) {
			if(g2[u][i] != INF) {
				if(d2[i] > d2[u]+g2[u][i]) {
					d2[i] = d2[u]+g2[u][i];
					if(!inq[i]) {
						inq[i] = true;
						que.push(i);
					}
				}
				if(d2[i] > d1[u]+g2[u][i]) {
					d2[i] = d1[u]+g2[u][i];
					if(!inq[i]) {
						inq[i] = true;
						que.push(i);
					}
				}
			}
			if(g1[u][i] != INF) {
				ll v = g1[u][i]*g1[u][i];
				// cout << v << endl;
				if(d1[i] > d2[u]+v) {
					d1[i] = d2[u]+v;
					if(!inq[i]) {
						inq[i] = true;
						que.push(i);
					}
				}
			}
		}
	}
	cout << min(d1[n],d2[n]) << endl;
}
int main() {
	// memset(g1,INF,sizeof g1);
	// memset(g2,INF,sizeof g2);
	scanf("%d%d", &n, &m);
	for(int i = 0; i <= n; ++i) {
		d1[i] = d2[i] = INF;
		for(int j = 0; j <= n; ++j) {
			g1[i][j] = g2[i][j] = INF;
		}
	}
	int t,a,b,c;
	for(int i = 0; i < m; ++i) {
		scanf("%d%d%d%d", &t, &a, &b, &c);
		if(t == 1 && g1[a][b] > c)
			g1[a][b] = g1[b][a] = c;
		else if(t == 0 && g2[a][b] > c)
			g2[a][b] = g2[b][a] = c;
	}
	
		for(int i = 1; i <= n; ++i) 
			for(int j = i+1; j <= n; ++j) 
				for(int k = 1; k <= n; ++k){
					if(k == i || j == k)
						continue;
					if(g1[i][j] > g1[i][k]+g1[k][j])
						g1[j][i] = g1[i][j] = g1[i][k]+g1[k][j];
				// cout << g1[i][j] << endl;
			}
	spfa();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值