题解:洛谷 P3115 Cow Routing

【题目来源】

洛谷:P3115 [USACO15JAN] Cow Routing S

【题目描述】

Bessie 对她农场那寒冷的天气感到厌烦,于是她准备坐飞机到一个更温暖的地方度假。不幸的是,她发现只有一个航空公司:Air Bovinia,愿意卖票给牛,并且这些票的结构有些复杂。

Air Bovinia 拥有 \(n\) 架飞机,每架飞机都有一个经过两个及以上的城市的特殊航线。举个例子:一架飞机可以从城市 \(1\) 出发,然后飞往城市 \(5\),再飞到城市 \(2\),最后飞到城市 \(8\)。注意航线是单向的。任何城市都不会在同一条航线上出现多次。如果 Bessie 选择了一条航线,那么她可以从航线上的任意一个城市上飞机,然后在途中任意一个城市下飞机。她不必从航线的起点上飞机,再从航线的终点下飞机。每条航线都有一个确定的花费,只要她搭乘了这个航班,她就必须支付这个航班的全额花费,不论她途经了几个城市。如果 Bessie 多次搭乘了某个航班,那么每次搭乘 Bessie 都必须支付航班的花费。

Bessie 想要找到从她农场所在的城市(城市 \(A\))到她目的地所在城市(城市 \(B\))最便宜的路线。请你告诉她他最少要花多少钱,并告诉她在此基础上她最少要经过几段航线,也即经过的城市数量 \(-1\)(包括起点和终点)。

【输入】

第一行三个整数 \(A,B,n\)

接下来 \(2n\) 行,每两行描述一次航班。第一行包括航线的花费 \(cost\) 与经过的城市数 \(len\)。第二行 \(len\) 个整数,表示航班依次经过的城市。

【输出】

一行两个整数,输出 Bessie 旅行最少要花的钱以及在此基础上最少的经过航线数量。

如果 Bessie 不能从她的农场到达她的目的地,则输出 -1 -1

【输入样例】

3 4 3
3 5
1 2 3 4 5
2 3
3 5 4
1 2
1 5

【输出样例】

2 2

【核心思想】

  1. 问题分析:给定 \(N\) 条航线,每条航线包含一系列城市序列,花费为 \(cost\)。Bessie 可以从航线上的任意城市上飞机,在之后的任意城市下飞机,每次搭乘支付全额 \(cost\)。求从城市 \(A\) 到城市 \(B\) 的最小花费,在花费最少的前提下经过的城市数最少。这是一个双目标最短路问题,关键在于将航线转化为城市之间的直达边,然后用 SPFA 按花费优先、其次城市数最少的策略求最短路。

  2. 算法选择

    • 航线拆边:每条航线上任意两个城市 \(x < y\) 之间建立有向边,花费为 \(cost\),经过城市数为 \(y - x\)
    • SPFA 双目标最短路:用 pair<int, int> 存储距离(花费,城市数),松弛时先比较花费,花费相同则比较城市数
  3. 关键步骤

    • 初始化
      • 读取 \(s\)(起点)、\(t\)(终点)、\(n\)(航线数)
      • 初始化航班矩阵 \(g[u][v]\)\(\infty\)
    • 航线拆边
      • 对每条航线,读取 \(cost\)(花费)和 \(k\)(城市数),以及城市序列 \(a[1..k]\)
      • 枚举 \(x\)\(1\)\(k\)\(y\)\(x+1\)\(k\)
        • \(g[a[x]][a[y]] = \min(g[a[x]][a[y]],\ \{cost,\ y-x\})\)
        • 保留花费最小、花费相同则城市数最少的边
    • SPFA 求最短路
      • 初始化 \(\text{dis}[s] = \{0, 0\}\),其余为 \(\infty\)
      • 队列中加入起点 \(s\)
      • 取出队首 \(u\),遍历所有可达城市 \(v\)
        • 新距离 \(\text{new\_dis} = \{\text{dis}[u].\text{first} + g[u][v].\text{first},\ \text{dis}[u].\text{second} + g[u][v].\text{second}\}\)
        • \(\text{dis}[v] > \text{new\_dis}\)(花费更小,或花费相同城市数更少),更新并入队
    • 输出:若 \(\text{dis}[t]\)\(\infty\),输出 -1 -1;否则输出花费和城市数
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot K^2 + V \cdot E)\)\(K\) 为每条航线最大城市数(\(\le 100\)),\(V \le 1000\)\(E \le N \cdot K^2\)
    • 空间复杂度:\(O(V^2)\),航班矩阵
  5. 双目标 SPFA 的核心思想

    • 航线拆边策略:将"从航线任意点上、任意点下"转化为城市间的直达边,避免复杂的状态设计
    • pair 比较规则pair 的默认比较先比 first(花费)再比 second(城市数),天然支持双目标优先级
    • SPFA 松弛:与普通 SPFA 相同,只是距离类型变为 pair,比较规则自动处理双目标
    • 不可达判断:用极大值判断是否到达终点
    • 适用于双目标最短路、航线换乘、花费与路径长度权衡类问题

【算法标签】

普及+ #SPFA

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用long long防止花费累加溢出
typedef pair<int, int> PII; // 定义对类型:first为花费,second为经过城市数
const int N = 1005; // 最大城市编号
int s, t, n; // s:起点城市, t:终点城市, n:航线数量
int a[N]; // 临时存储每条航线上的城市序列
// g[u][v]:从城市u到城市v的最优航班信息(最小花费,最少经过城市数)
PII g[N][N];
// dis[u]:从起点s到城市u的最小花费和最少经过城市数
PII dis[N];

// SPFA算法:求最短路(按花费优先,花费相同则城市数最少)
void spfa(int s)
{
	// 初始化距离为无穷大
	memset(dis, 0x3f, sizeof(dis));
	queue<int> q; // SPFA队列
	dis[s] = {0, 0}; // 起点花费为0,经过城市数为0
	q.push(s);
	while (!q.empty())
	{
		int u = q.front(); q.pop(); // 取出队首城市
		// 遍历所有可能到达的城市v
		for (int v=1; v<=1000; v++)
		{
			// 计算从u经航班到v的新距离
			// 新花费 = 到u的花费 + u到v的航班花费
			// 新城市数 = 到u的城市数 + u到v经过的城市数
			PII new_dis = {dis[u].first+g[u][v].first, dis[u].second+g[u][v].second};
			// 如果新距离更优(花费更小,或花费相同但城市数更少)
			if (dis[v]>new_dis)
			{
				dis[v]=new_dis; // 更新距离
				q.push(v); // v入队
			}
		}
	}
}

signed main()
{
    cin >> s >> t >> n; // 读入起点、终点和航线数
    // 初始化航班矩阵为无穷大
    memset(g, 0x3f, sizeof(g));
    for (int i=1; i<=n; i++) // 读入每条航线
    {
    	int cost, k; // cost:航班花费, k:航线包含城市数
    	cin >> cost >> k;
    	for (int j=1; j<=k; j++) // 读入航线上的城市序列
    		cin >> a[j];
    	// 航线上的任意两个城市x<y之间都可以直达
    	// 花费为cost,经过城市数为y-x(中间经过的城市数)
    	for (int x=1; x<=k; x++)
    		for (int y=x+1; y<=k; y++)
    			// 保留花费最小、花费相同则城市数最少的方案
    			g[a[x]][a[y]] = min(g[a[x]][a[y]], {cost, y-x});
    }
    spfa(s); // 从起点s运行SPFA
    // 如果终点不可达,输出-1 -1
    if (dis[t].first>=1e18) cout << -1 << " " << -1 << endl;
    // 否则输出最小花费和最少经过城市数
    else cout << dis[t].first << " " << dis[t].second << endl;
    return 0;
}

【运行结果】

3 4 3
3 5
1 2 3 4 5
2 3
3 5 4
1 2
1 5
2 2
posted @ 2026-08-30 09:16  团爸讲算法  阅读(7)  评论(0)    收藏  举报