【题解】[JLOI2011] 飞行路线

题目信息

题目来源:CCF 吉林省选 2011;

在线评测地址:Luogu#4568BZOJ#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 了、
}
posted @ 2020-10-23 21:49  5ab  阅读(76)  评论(0编辑  收藏  举报