## [$AcWing$ $340$. 通信线路](https://www.acwing.com/problem/content/342/) ### 一、题目描述 在郊区有 $N$ 座通信基站,$P$ 条 **双向** 电缆,第 $i$ 条电缆连接基站 $A_i$ 和 $B_i$。 特别地,**$1$号基站是通信公司的总站** (起点),**$N$号基站** (终点) 位于一座农场中。 现在,农场主希望对通信线路进行升级,其中升级第 $i$ 条电缆需要花费 $L_i$ 电话公司正在举行优惠活动 农产主可以指定一条从 $1$ 号基站到 $N$ 号基站的路径,并指定路径上不超过 $K$ 条电缆,由电话公司 **免费** 提供升级服务 农场主只需要支付在该路径上 **剩余的电缆中**,**升级价格最贵** 的那条电缆的花费即可 求 **至少用多少钱** 可以完成升级 **输入格式** 第 $1$ 行:三个整数 $N,P,K$。 第 $2..P+1$ 行:第 $i+1$ 行包含三个整数 $A_i,B_i,L_i$。 **输出格式** 包含一个整数表示最少花费。 若 $1$ 号基站与 $N$ 号基站之间不存在路径,则输出 $−1$。 **数据范围** $0≤K **如果给我$mid$元钱,我有没有办法确定这么多钱能否够完成升级一条线路呢?** > 这个简单,我们可以视真实边权大于$mid$的设置 **虚拟边权** 为$1$,反之设为$0$ > 然后在这个图上用$Dijkstra$求最短路径,也就是最短路径长度: > - 如果最短路径的长度值大于$k$,说明$mid$小了,再调大一点 > - 如果最短路径的长度值不大于$k$,说明$mid$大了,再调小一点 噢,原来需要 **二分答案** ### 三、$Code$ ```cpp {.line-numbers} #include using namespace std; typedef pair PII; const int INF = 0x3f3f3f3f; const int N = 1010; // 1000个点 const int M = 20010; // 10000条,记录无向边需要两倍空间 int idx, h[N], e[M], w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } int n; //点数 int m; //边数 bool st[N]; //记录是不是在队列中 int k; //不超过K条电缆,由电话公司免费提供升级服务 int dist[N]; //记录最短距离 // u指的是我们现在选最小花费 bool check(int x) { memset(st, false, sizeof st); memset(dist, 0x3f, sizeof dist); priority_queue, greater> q; dist[1] = 0; q.push({0, 1}); while (q.size()) { PII t = q.top(); q.pop(); int d = t.first, u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i], v = w[i] > x; //如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0 if (dist[j] > d + v) { dist[j] = d + v; q.push({dist[j], j}); } } } //如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达 if (dist[n] == INF) { puts("-1"); //不可达,直接输出-1 exit(0); } return dist[n] <= k; //如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法 } int main() { memset(h, -1, sizeof h); cin >> n >> m >> k; int a, b, c; for (int i = 0; i < m; i++) { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } /*这里二分的是直接面对答案设问:最少花费 依题意,最少花费其实是所有可能的路径中,第k+1条边的花费 如果某条路径不存在k+1条边(边数小于k+1),此时花费为0 同时,任意一条边的花费不会大于1e6 整理一下,这里二分枚举的值其实是0 ~ 1e6*/ int l = 0, r = 1e6; while (l < r) { int mid = (l + r) >> 1; if (check(mid)) // check函数的意义:如果当前花费可以满足要求,那么尝试更小的花费 r = mid; else l = mid + 1; } printf("%d\n", l); return 0; } ```