电话线

电话线

二分答案+最短路

问题描述

​ 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.题目没有说明是有向图,那就是无向图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值