题目
题目来源/版权:USACO 2008 January Silver;
题目描述
Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system.
FJ 想让他的农场通电话。不幸的是,电话公司不合作,因此他需要支付将农场连接到电话系统所需的一些电缆的费用。
There are N forlorn telephone poles conveniently numbered 1..N that are scattered around Farmer John's property; no cables connect any them. A total of P (1 ≤ P ≤ 10,000) pairs of poles can be connected by a cable; the rest are too far apart.
在 FJ 的房屋周围散布着 (N) 个没有电缆连接的电线杆,编号为 (1) 到 (N)。其中有 (P) 对电线杆可以连接,其余的相距太远。
The i-th cable can connect the two distinct poles Ai and Bi, with length Li (1 ≤ Li ≤ 1,000,000) units if used. The input data set never names any {Ai, Bi} pair more than once. Pole 1 is already connected to the phone system, and pole N is at the farm. Poles 1 and N need to be connected by a path of cables; the rest of the poles might be used or might not be used.
第 (i) 根电缆可以连接电线杆 (A_i) 和 (B_i),长度为 (L_i) 个单位。(1) 号杆已经连接到电话系统,而 (N) 号杆则连接着 FJ 的农场。(1) 号杆和 (N) 号杆需要通过电缆路径连接;其余电线杆可能使用或未使用。
As it turns out, the phone company is willing to provide Farmer John with K lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.
事实证明,电话公司愿意免费为 FJ 提供 (K) 根电缆。除此之外,他将必须支付的价格等于剩余的电缆中最长的电缆的长度(每对电线杆都连接有单独的电缆),或者如果他不需要任何其他电缆,则为 (0)。
Determine the minimum amount that Farmer John must pay.
计算出 FJ 需要支付的最少费用。
输入格式
- Line 1: Three space-separated integers: N, P, and K
- Lines 2..P+1: Line i+1 contains the three space-separated integers: Ai, Bi, and Li
第一行是三个整数:(N)、(P)、(K),中间由空格分隔;
接下来 (P) 行,每行有三个用空格分隔的整数:(A_i)、(B_i)、(L_i)。
输出格式
- Line 1: A single integer, the minimum amount Farmer John can pay. If it is impossible to connect the farm to the phone company, print -1.
一行,一个整数,表示 FJ 需要支付的最小费用。如果不存在从 (1) 号杆到 (N) 号杆的路径,输出 -1
。
数据范围与约定
(0le K<Nle 1000),(1le Ple 10000),(1le L_ile 10^6),(A_i eq B_i)。
数据保证不存在重边。
分析
题目大意是说,给你一张 无向图,求出所有路径上第 (k+1) 大的边权的最小值。
这道题的条件是「最大边权的最小值」,这类问题(指「极大的极小」或「极小的极大」)常用二分答案解决。
也就是说,我们的问题成为了「判断是否存在一条路径,使得路径上边权第 (K+1) 大的边小于等于 (u)」的问题((u) 是二分时的数)。
我们可以把这个问题转换成「是否存在一条路径,使得路径上有不超过 (K) 条边的边权大于 (u)」。
这就很简单了,只需将实际边权大于 (u) 的边赋予 (1) 的权值,其它的边均为 (0),跑 (1) 到 (n) 的最短路,结果小于等于 (K) 就可行。复杂度 (O((N+P)log P imeslog L_i)sim 10^{4} imes 14 imes 20approx 3 imes 10^6),绰绰有余。
顺带一提,如果是边权 (0/1) 的最短路是可以用双端队列 BFS 来优化的,可以达到 (O(n+m)) 的复杂度。
Code
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int max_n = 1000, max_m = 10000, INF = 1048576;
struct node
{
int id, val;
node(int _i = 0, int _v = 0) : id(_i), val(_v) { }
bool operator<(const node& a) const { return val > a.val; }
};
int hd[max_n], des[max_m<<1], val[max_m<<1], nxt[max_m<<1], dis[max_n], edg_cnt = 0, n, lim;
bool vis[max_n];
priority_queue<node> pq;
void add_edge(int s, int t, int v)
{
des[edg_cnt] = t, val[edg_cnt] = v;
nxt[edg_cnt] = hd[s], hd[s] = edg_cnt++;
}
bool is_conn(int upp)
{
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
node cur;
dis[0] = 0;
pq.push(node());
while (!pq.empty())
{
cur = pq.top();
pq.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] > upp))
{
dis[des[p]] = dis[cur.id] + (val[p] > upp);
if (!vis[des[p]])
pq.push(node(des[p], dis[des[p]]));
}
}
}
return dis[n-1] <= lim;
}
int main()
{
memset(hd, -1, sizeof(hd));
int m, ta, tb, tc, lb = 0, ub = INF, ans = -1, mid;
scanf("%d%d%d", &n, &m, &lim);
for (int i = 0; i < m; i++)
{
scanf("%d%d%d", &ta, &tb, &tc);
add_edge(ta - 1, tb - 1, tc);
add_edge(tb - 1, ta - 1, tc);
}
while (lb < ub)
{
mid = (lb + ub) >> 1;
if (is_conn(mid))
ans = mid, ub = mid;
else
lb = mid + 1;
}
printf("%d
", ans);
return 0;
}
后记
还记得当时这道题是我考试时的 T4,前三题都是秒切的,就这道题卡了一个小时多都没搞出正解。主要还是因为当时不会二分答案。
现在过了一年,再来看这道题,记忆就很深刻,做出来也挺有感觉的,真是挺怀念那时的 OIers(不行,不能再扯了)。