通信(二分+SPFA好题)
第1题 通信 查看测评数据信息
某城市有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站 Ai 和 Bi。特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 Li。电话公司正在举行优惠活动。农场主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。
求至少用多少钱可以完成升级。0≤K<N≤1000,1≤P≤10000 ,1≤Li≤1000000
输入格式
第 1 行:三个整数 N,P,K。
第 2..P+1 行:第 i+1 行包含三个整数 Ai,Bi,Li。
输出格式
包含一个整数表示最少花费。
若 1 号基站与 N 号基站之间不存在路径,则输出 −1
输入/输出例子1
输入:
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
输出:
4
样例解释
无
分析一下,化简题意就是,1走到n,砍掉一些边,求出剩余边的最大值,使这个最大值最小
1走到5,进过1-3(4),3-2(3),2-5(9)
砍掉9,答案是4(4,3的最大值)
那么应该怎么写呢?
我们可以用二分查找,二分砍掉的边的权值。注意,二分不仅仅可以找已知的量,即使不知道这个量也可以二分
例如我们设k=1,二分砍掉的值是5,那么路上遇到>5的值,就设置这条边的权值为1,否则权值为0
4<=5,权为0;5<=5,权为0;6>5,权为1
那么此图最短路是1,(0+0+1),1<=k,答案有效,所以我们选择砍掉6的边。(如果二分到的值为7,此时应该缩小答案)
但是如果二分砍掉的值是4
4<=4,权为0;5>4,权为1;6>4,权为1
那么最短路会是2(0+1+1),2>k,答案无效。那么此时应该增大答案
非常的巧妙,于是代码出来了
#include <bits/stdc++.h> using namespace std; const int N=1e3+5; struct node { int v, w; }; int n, p, k, u1, v1, w1, Max=0, dis[N], vis[N], L=0, R=0, ans=-1; vector<node> a[N]; queue<int> q; void spfa(int s) { memset(dis, 63, sizeof dis); memset(vis, 0, sizeof vis); dis[1]=0, q.push(1); while (!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for (int i=0; i<a[u].size(); i++) { int v=a[u][i].v, w=a[u][i].w; if (dis[v]>dis[u]+(w>s)) //w是边权 s是二分的值 w>s 则设置权为1(需要砍掉),否则为0 { dis[v]=dis[u]+(w>s); if (!vis[v]) vis[v]=1, q.push(v); } } } } bool check(int val) { // cout<<val<<endl; spfa(val); if (dis[n]>k) return false; //砍的边数>k,无效 return true; //<=k有效 } int main() { scanf("%d%d%d", &n, &p, &k); for (int i=1; i<=p; i++) { scanf("%d%d%d", &u1, &v1, &w1); a[u1].push_back({v1, w1}); a[v1].push_back({u1, w1}); Max=(Max, w1); //记录最大边权 } L=0, R=Max; //有可能全砍,所以L=0,有可能全不砍,所以R=最大边权 while (L<=R) { int mid=(L+R)/2; if (check(mid)) ans=mid, R=mid-1; //发现答案有效,即可缩小右端点,找一个更小值 else L=mid+1; //否则继续扩大,找一个更大的值 } printf("%d", ans); //默认-1 return 0; }