[USACO08JAN]Telephone Lines S

题目链接: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值