题目链接:https://www.luogu.com.cn/problem/P1948
题意: n个点,p条边,政府可以给免费修k条路,问你找到一条从1号点到n号点的最短路径长度,而这条最短路的代价是这条路径上的花费最大的价钱(除去至多k条最大的价钱之后)
下面是这个题中最重要的信息:
电信公司决定支援灾区免费为此市连接k对由笨笨指定的电话线杆,对于此外的那些电话线,需要为它们付费,总费用决定于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。如果需要连接的电话线杆不超过k对,那么支出为0.
题解: 首先这个题让求的总费用是最长的电话线的长度
假设答案是ans,那么答案肯定是在0到最大花费+1之间,其实答案应该是在0到最大花费之间,而我这里说在0到最大花费+1,是为了如果答案是最大花费+1,那么这个答案就是-1,也就是题目中的不可能完成;
接着我们考虑0 <= ans <= maxval
;
下面我们就对这个区间进行二分,mid = l + r;
如果mid符合的话,那么肯定可以找到一条路,这条路上的大于mid的个数肯定是小于等于k的(答案就在l 到mid之间),否则mid就是不符合,那么答案肯定是在mid + 1 到 r之间;
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1010;
vector<pair<int,int>>v[N]; // 建立邻接表
int n,m,k;
bool st[N];
// dijkstra
bool check(int cost)
{
memset(st,0,sizeof st);
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
q.push({0,1});
while(q.size())
{
auto t = q.top();q.pop();
int dis = t.first;
int p = t.second;
st[p] = true;
for(auto &x : v[p])
{
// 如果当前的边权大于我们的答案,那么这条边就让政府来修
if(x.second > cost){
// 如果政府能修的路还没达到k条,那么就让他修
if(t.first + 1 <= k && !st[x.first]){
// 如果找到了终点,那么一定要提前返回,通过出队来判断终点的话会超时
if(x.first == n){
return true;
}
q.push({t.first + 1,x.first});
}
}else{
// 如果当前的边权不大于我们的答案,那么这条边就可以直接走,对我们的答案并不产生影响
if(!st[x.first]){
if(x.first == n){
return true;
}
q.push({t.first,x.first});
}
}
}
}
return false;
}
int main()
{
// 输入n,m,k
scanf("%d%d%d",&n,&m,&k);
int l = 0,r = 0;
// 左边界l,右边界r
for(int i = 0;i < m;i ++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
v[a].push_back({b,c});
v[b].push_back({a,c});
r = max(r,c);
}
int kkk = r + 1;
r ++;
// 二分
while(l < r)
{
int mid = l + r>> 1;
if(check(mid)){
r = mid;
}else{
l = mid + 1;
}
}
if(r == kkk){
printf("%d\n",-1);
}
else
printf("%d\n",r);
return 0;
}