P1948 [USACO08JAN] Telephone Lines S
题目描述
多年以后,笨笨长大了,成为了电话线布置师。由于地震使得某市的电话线全部损坏,笨笨是负责接到震中市的负责人。该市周围分布着 nnn(1≤n≤1031\le n\le10^31≤n≤103)根按 1∼n1\sim n1∼n 顺序编号的废弃的电话线杆,任意两根线杆之间没有电话线连接,一共有 ppp(1≤p≤1041\le p\le10^41≤p≤104)对电话杆可以拉电话线。其他的由于地震使得无法连接。
第i对电线杆的两个端点分别是 ai,bia_i,b_iai,bi,它们的距离为lil_ili(1≤li≤1061\le l_i\le10^61≤li≤106)。数据中每对 (ai,bi)(a_i,b_i)(ai,bi) 只出现一次。编号为 111 的电话杆已经接入了全国的电话网络,整个市的电话线全都连到了编号 nnn 的电话线杆上。也就是说,笨笨的任务仅仅是找一条将 111 号和 nnn 号电线杆连起来的路径,其余的电话杆并不一定要连入电话网络。
电信公司决定支援灾区免费为此市连接 kkk (1≤k≤p1\le k\le p1≤k≤p)对由笨笨指定的电话线杆,对于额外的那些电话线,需要为它们付费,总费用决定于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。如果需要连接的电话线杆不超过 kkk 对,那么支出为 000。
请你计算一下,将电话线引导震中市最少需要在电话线上花多少钱?
输入格式
输入文件的第一行包含三个数字 n,p,kn,p,kn,p,k。
第二行到第 p+1p+1p+1 行,每行分别都为三个整数 ai,bi,lia_i,b_i,l_iai,bi,li。
输出格式
一个整数,表示该项工程的最小支出,如果不可能完成则输出 −1-1−1。
输入输出样例 #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
输出 #1
4
题目要求花费最小值,可以察觉到能利用二分答案求解。
我们记录下所有边中最大的修复代价作为 rrr。随后我们希望在 check 中检查当前的二分结果是否合法——找到一条最短路,其中需要进行修复的路小于等于 kkk。这里我们可以将代价转换为消耗的免费修复次数distdistdist。如果从 1 到 n 的dist<=kdist<=kdist<=k,则说明当前二分答案可行,check 返回 truetruetrue
#include<bits/stdc++.h>
using namespace std;
bool check(int mid, int n, int k, vector<vector<pair<int, int>>>& g){
vector<int> dist(n+1, INT_MAX);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
dist[1] = 0;
pq.emplace(0, 1);
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue;
for (auto& edge : g[u]) {
int v = edge.first;
int len = edge.second;
int cost = (len > mid) ? 1 : 0;
if (dist[v] > dist[u] + cost) {
dist[v] = dist[u] + cost;
pq.emplace(dist[v], v);
}
}
}
return dist[n]<=k;
}
int main(){
int n, p, k;
cin>>n>>p>>k;
vector<vector<pair<int, int>>> g(n+1);
int l = 0, r = 0, mid = 0;
for(int i = 0;i<p;i++){
int a, b, len;
cin>>a>>b>>len;
g[a].emplace_back(b, len);
g[b].emplace_back(a, len);
if(len>r) r = len;
}
int ans = -1;
while(l<=r){
mid = (l + r)/2;
if(check(mid, n, k, g)){
r = mid - 1;
ans = mid;
}else {
l = mid + 1;
}
}
cout<<ans;
return 0;
}