【SSL 2120】通信线路

题目大意

郊区有 n n n 座通信基站, p p p 条双向电缆,第 i i i 条电缆连接基站 A i A_i Ai B i B_i Bi。特别地, 1 1 1 号基站是通信公司的总站, n n n 号基站位于一座农场当中。现在,农场主希望对通信线路进行升级,其中升级第 i i i 条电缆需要花费 L i L_i Li
电话公司正在举行优惠活动。农场主可以指定一条从 1 1 1 号基站到 n n n 号基站的路径,并指定路径上不超过 k k k 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。

输入格式

第一行三个整数 n , p , k n,p,k n,p,k
接下来 p p p 行,每行三个正整数 a i , b i , l i a_i,b_i,l_i ai,bi,li

输出格式

仅包含一个整数,表示在该项目上的最小支出,若任务不可能完成,则输出 − 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

输出样例

4

基本思路

首先我们来转化一下这个问题,对于一条路径我们肯定是选择前 k k k 大的数进行免费,再在剩下的里面挑一个最大的。于是问题就转化为寻找一条路径使其中第 k + 1 k+1 k+1 大的数最小,这不明显的二分吗?

但是问题又来了,怎么检验呢?显然我们要验证是否存在一条路径第 k + 1 k+1 k+1 大的数是否小于等于我们选择的数(假设为 d = 6 d=6 d=6 ),我们可以把所有大于 d d d 的数标记为 1 1 1 ,小于等于 d d d 的数标记为 0 0 0
在这里插入图片描述

这样我们可以用最短路来寻找一条边权总和最小的路径,即大于 d d d 是需要动用免费的,路过时加 1 1 1,如此一来就可以找出当前情况下免费次数最少的路径,随后判断它是否大于 k k k 即可。

核心代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
struct node{
	int to,cost;
};
int n,p,k,l,r,mid,dis[N];
bool v[N];
vector<node>g[N];
deque<int>q;
inline bool check(int x){
	//spfa
	memset(dis,0x3f,sizeof(dis));
	memset(v,0,sizeof(v));
	dis[1]=0;v[1]=true;
	q.push_back(1);
	while(!q.empty()){
		int u=q.front();
		q.pop_front();
		v[u]=false;//允许重复激活 
		for(node i:g[u]){
			int j=i.to,w=i.cost>x;//w为0或1
			if(dis[j]>dis[u]+w){
				dis[j]=dis[u]+w;
				if(v[j]==true) continue;
				v[j]=true;
				if(w==0)
					q.push_front(j);
				else
					q.push_back(j);
			}
		}
	}
	if(dis[n]>k) return false;
	return true;
}
int main(){
	ios::sync_with_stdio(false);
	//寻找一条路径使得其中第k+1大的权值最小
	//二分第k+1大的权值,假设为6,则途中大于6的值标记为1
	//代表经过这条边需要一次免费,路过时统计免费次数,小于等于6标记为0
	//验证时双端队列让为0的边优先扩展
	cin>>n>>p>>k;
	for(int i=1,ai,bi,li;i<=p;i++){
		cin>>ai>>bi>>li;
		g[ai].push_back({bi,li});
		g[bi].push_back({ai,li});
	}
	l=0,r=1e6+1;
	//为什么是1e6+1?因为可能1和n根本不连通
	while(l<r){
		mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	if(l==1e6+1)
		l=-1;
	cout<<l;
	return 0;
}
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值