【题解】[JLOI2011] 飞行路线
题目信息
题目来源:CCF 吉林省选 2011;
在线评测地址:Luogu#4568,BZOJ#2763;
运行限制:时间不超过 \(1.00\ \textrm{ms}\),空间不超过 \(128\ \textrm{MiB}\)。
题目描述
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 \(n\) 个城市设有业务,设这些城市分别标记为 \(0\) 到 \(n−1\),一共有 \(m\) 种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 \(k\) 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?
输入格式
第一行三个整数 \(n\)、\(m\)、\(k\),分别表示城市数,航线数和免费乘坐次数。
接下来一行两个整数 \(s\)、\(t\),分别表示他们出行的起点城市编号和终点城市编号。
接下来 \(m\) 行,每行三个整数 \(a\)、\(b\)、\(c\),表示存在一种航线,能从城市 \(a\) 到达城市 \(b\),或从城市 \(b\) 到达城市 \(a\),价格为 \(c\)。
输出格式
输出一行一个整数,为最少花费。
数据规模与约定
- 对于 \(30\%\) 的数据,\(2\le n\le 50\),\(1\le m\le 300\),\(k=0\);
- 对于 \(50\%\) 的数据,\(2\le n\le 600\),\(1\le m\le 6\times 10^3\),\(0\le k\le 1\);
- 对于 \(100\%\) 的数据,\(2\le n\le 10^4\),\(1\le m\le 5\times 10^4\),\(0\le k\le 10\)。
保证 \(0\le s,t,a,b\le n\),\(a\ne b\),\(0\le c\le 10^3\)。
分析
题意很明确,给定一张图,一条路径上最多可以使 \(k\) 条边权值为 \(0\),求最短路。
请注意这道题与 [USACO18JAN-Sliver] Telephone Lines 的区别。这道题的最短路是求和,而那一道题的最短路则是取最大值。这两个区别导致这两题本质不同。
由于有用了多少条边的信息,我们可以构造分层图。第 \(i\) 层(\(0\le i\le n\))为用了 \(i\) 次使边权为 \(0\) 的操作。
只需要使起点为 \((s,0)\),求解 \((t,0),(t,1),\cdots ,(t,k)\) 的最短路并去最小值即可。
注意事项
这道题的图是无向图,注意加双向边,同时开两倍空间。
Code
图论题,尤其是最短路的代码还是比较好敲的,注意一下细节就行了。
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int max_n = 10000, max_m = 50000, max_k = 10, max_l = max_k + 1;
struct node
{
int id, val;
node(int _i = 0, int _v = 0) : id(_i), val(_v) { }
bool operator<(const node& n) const
{
return val > n.val;
}
};
int hd[max_n*max_l], des[max_m*(max_k+max_l)*2], val[max_m*(max_k+max_l)*2], nxt[max_m*(max_k+max_l)*2], e_cnt = 0; // 存图(2*空间)
int dis[max_n*max_l], n;
bool vis[max_n*max_l] = {};
priority_queue<node> q;
inline int mk(int id, int ly) { return id + ly * n; } // 生成节点编号
#define gc getchar
inline int read() // 快读
{
int c = gc(), t = 1, n = 0;
while (isspace(c)) { c = gc(); }
if (c == '-') { t = -1, c = gc(); }
while (isdigit(c)) { n = n * 10 + c - '0', c = gc(); }
return n * t;
}
#undef gc
void add_edge(int s, int t, int v)
{
des[e_cnt] = t, val[e_cnt] = v;
nxt[e_cnt] = hd[s], hd[s] = e_cnt++;
}
int main()
{
memset(hd, -1, sizeof(hd));
memset(dis, 0x3f, sizeof(dis));
n = read();
int m = read(), k = read(), s = read(), t = read(), ta, tb, tc, ans = 0x3f3f3f3f;
node cur;
for (int i = 0; i < m; i++)
{
ta = read(), tb = read(), tc = read();
for (int j = 0; j < k; j++)
{
add_edge(mk(ta, j), mk(tb, j), tc); // 层内的边
add_edge(mk(tb, j), mk(ta, j), tc);
add_edge(mk(ta, j), mk(tb, j+1), 0); // 使用一次操作,跨层
add_edge(mk(tb, j), mk(ta, j+1), 0);
}
add_edge(mk(ta, k), mk(tb, k), tc); // 注意总共有 k+1 层
add_edge(mk(tb, k), mk(ta, k), tc);
}
q.push(node(mk(s, 0), 0));
dis[mk(s,0)] = 0;
while (!q.empty()) // 最短路
{
cur = q.top();
q.pop();
if (!vis[cur.id])
{
vis[cur.id] = true;
for (int p = hd[cur.id]; p != -1; p = nxt[p])
if (dis[des[p]] > dis[cur.id] + val[p])
{
dis[des[p]] = dis[cur.id] + val[p];
if (!vis[des[p]])
q.push(node(des[p], dis[des[p]]));
}
}
}
for (int i = 0; i <= k; i++)
if (dis[mk(t,i)] < ans)
ans = dis[mk(t, i)]; // 统计最小答案
printf("%d\n", ans); // 输出
return 0; // 然后就 AC 了、
}
本文来自博客园,作者 5ab,转载请注明链接哦 qwq
博客迁移啦,来看看新博客吧 -> https://5ab-juruo.oier.space/

浙公网安备 33010602011771号