题目描述
在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站Ai和Bi。
特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。
现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费Li。
电话公司正在举行优惠活动。
农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。
农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。
求至少用多少钱可以完成升级。
输入格式
第1行:三个整数N,P,K。
第2..P+1行:第 i+1 行包含三个整数Ai,Bi,Li。
输出格式
包含一个整数表示最少花费。
若1号基站与N号基站之间不存在路径,则输出”-1”。
数据范围
0≤K<N≤1000,
1≤P≤10000,
1≤Li≤1000000
样例
Input
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
Output
4
算法
二分 + 最短路
当从结点$1$到结点$N$的所有路径中存在小于等于$K$条边的路径时,升级费用为0;不存在小于等于$K$条边的路径时,升级费用为所有可能的路径中第$K + 1$大权值的最小值(对于每一条路径而言,让电话公司免费升级权值大小为$1$到$K$的边显然最优)。
直接对结果$R$(所求的边权)进行二分,那么只要存在一条路径使得路径上边权大于等于$R$的边的数量小于等于$K$,则$R$是合法的(但不一定最小);反之,若不存在,则$R$不合法。若$R$为合法的最优解,对于$\ge R$的所有值均合法,对于$<R$的所有值均不合法,符合二分的适用条件。
关于二分初始条件的设置,$l = 0, r = 1e6 + 1$,$r$为设置的哨兵,当不存在从$1$至$N$的路径时,二分返回的结果为$1e6 + 1$。
因为问题转化为求边权为$0$或$1$的图的最短路问题,可以使用双端队列优化dijkstra。
时间复杂度
$O(P \times logP \times logLi)$ //堆优化版本dijkstra
$O(P \times logLi)$ //双端队列优化dijkstra
C++ 代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <deque>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1010, M = 20010;
int h[N], va[M], ne[M], we[M], idx;
int st[N];
int d[N];
int n, m, k;
void insert(int u, int v, int w) {
va[++idx] = v, we[idx] = w, ne[idx] = h[u], h[u] = idx;
}
bool check(int x) { //双端队列
memset(st, 0, sizeof st);
memset(d, 0x3f, sizeof d);
d[1] = 0;
deque<int> q;
q.push_back(1);
while (!q.empty()) {
int ver = q.front();
q.pop_front();
if (st[ver]) continue;
st[ver] = true;
//if (ver == n) return d[n] <= k;
for (int i = h[ver]; i; i = ne[i]) {
int j = va[i], w = we[i] > x;
if (!st[j] && d[j] > d[ver] + w) {
if (w) q.push_back(j);
else q.push_front(j);
d[j] = d[ver] + w;
}
}
}
return d[n] <= k;
}
// bool check(int x) { //堆优化dijkstra
// memset(st, 0, sizeof st);
// memset(d, 0x3f, sizeof d);
// d[1] = 0;
// priority_queue<PII, vector<PII>, greater<PII> > heap;
// heap.push({0, 1});
// while (!heap.empty()) {
// PII t = heap.top();
// heap.pop();
// int ver = t.second;
// if (st[ver]) continue;
// st[ver] = true;
// for (int i = h[ver]; i; i = ne[i]) {
// int j = va[i], w = we[i] > x;
// if (d[j] > d[ver] + w) {
// d[j] = d[ver] + w;
// heap.push({d[j], j});
// }
// }
// }
// return d[n] <= k;
// }
int main() {
scanf("%d%d%d", &n, &m, &k);
while (m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
insert(u, v, w);
insert(v, u, w);
}
int l = 0, r = 1000001;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1000001) printf("%d\n", -1);
else printf("%d\n", l);
return 0;
}