电话线
二分答案+最短路
问题描述
Farmer John打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务。于是,FJ必须为此向电信公司支付一定的费用。
FJ的农场周围分布着N根按1..N顺次编号的废弃的电话线杆,任意两根电话线杆间都没有电话线相连。一共P对电话线杆间可以拉电话线,其余的那些由于隔得太远而无法被连接。
第i对电话线杆的两个端点分别为A_i、B_i,它们间的距离为L_i 。数据中保证每对{A_i,B_i}最多只出现1次。编号为1的电话线杆已经接入了全国的电话网络,整个农场的电话线全都连到了编号为N的电话线杆上。也就是说,FJ的任务仅仅是找一条将1号和N号电话线杆连起来的路径,其余的电话线杆并不一定要连入电话网络。
经过谈判,电信公司最终同意免费为FJ连结K对由FJ指定的电话线杆。对于此外的那些电话线,FJ需要为它们付的费用,等于其中最长的电话线的长度(每根电话线仅连结一对电话线杆)。如果需要连结的电话线杆不超过K对,那么FJ的总支出为0。
请你计算一下,FJ最少需要在电话线上花多少钱。
输入格式
第一行输入3个用空格隔开的整数:N、P、以及K
以下P行中,第i行为3个用空格隔开的整数:A_i、B_i、L_i。
输出格式
输出一个整数,为FJ在这项工程上的最小支出。
如果任务不可能完成,输出-1。
思路
FJ要在所有方案中选择电话线花费最大的最小值(除开电力公司已经支付的电话线)。二分答案:二分FJ在电话线上的花费。
怎么判断当前二分的答案mid合不合理:
电力公司要支付的电线用二维数组G记录。如果某根电话线的长度>=mid,那么电力公司就会支付,G[i][j]=G[j][i]=1,表示电力公司要支付这根电话线;否则就赋值为0,表示不支付。G数组跑一遍最短路,就是电力公司至少应该支付的电话线的数量。如果支付数量>K,说明这个答案不合理,增大下界;反则减小上界。
代码
#include<iostream>
#include<cstdio>
#include<queue>
#define N 50000
#define Inf 1e9
using namespace std;
int n,p,k;
int NextM[N],LastM[N],LenM[N],EndM[N],cntM;
int Next[N],Last[N],Len[N],End[N],dis[N],cnt;
bool used[N],usedM[N];
struct node{
int id,len;
bool operator<(const node &a)const{
return len>a.len;
}
};
//建图:电话线的布局
void InsM(int x,int y,int l){
EndM[++cntM]=y;
LenM[cntM]=l;
NextM[cntM]=LastM[x];
LastM[x]=cntM;
}
//建图:电力公司需要支付的电话线
void InsG(int x,int y,int l){
End[++cnt]=y;
Len[cnt]=l;
Next[cnt]=Last[x];
Last[x]=cnt;
}
void dijkstra(int s){
for(int i=1;i<N;i++)dis[i]=Inf;
priority_queue<node>q;
q.push((node){s,0}),dis[s]=0;
while(!q.empty()){
int u=q.top().id;q.pop();
if(used[u])continue;
used[u]=true;
for(int i=Last[u];i;i=Next[i]){
int v=End[i];
if(!used[v] && dis[v]>dis[u]+Len[i]){
dis[v]=dis[u]+Len[i];
q.push((node){v,dis[v]});
}
}
}
}
//初始化
void CleanG(){
for(int i=1;i<N;i++){
usedM[i]=false,used[i]=false;
Next[i]=false,Last[i]=false,End[i]=false;
}
}
//判断二分答案是否合理
bool Get(int mid){
cnt=0;
CleanG();
for(int i=1;i<=n;i++){
usedM[i]=true;
for(int j=LastM[i];j;j=NextM[j])
if(!usedM[EndM[j]]){
//注意:由于是边存储,所以即便花费<=mid也要加边;如果用二维数组存图就不需要
if(LenM[j]>mid)InsG(i,EndM[j],1),InsG(EndM[j],i,1);
else InsG(i,EndM[j],0),InsG(EndM[j],i,0);
}
}
dijkstra(1);
if(dis[n]<=k)return true;
else return false;
}
int main(){
int L=0,R=0,Max=0;
scanf("%d%d%d",&n,&p,&k);
for(int i=1;i<=p;i++){
int a,b,l;
scanf("%d%d%d",&a,&b,&l);
R=max(R,l),Max=R;
InsM(a,b,l),InsM(b,a,l);
}
while(L<=R){
int mid=((L+R)>>1);
if(Get(mid))R=mid-1;
else L=mid+1;
}
if(L>Max)puts("-1");
else printf("%d",L);
return 0;
}
总结
1.不是所有的最短路的题目都只考最短路
2.注意初始化
3.题目没有说明是有向图,那就是无向图