[USACO08JAN]电话线Telephone Lines题解

85 篇文章 0 订阅
49 篇文章 0 订阅

题目

多年以后,笨笨长大了,成为了电话线布置师。由于地震使得某市的电话线全部损坏,笨笨是负责接到震中市的负责人。该市周围分布着 N(1<=N<=1000) N ( 1 <= N <= 1000 ) 根据1……n顺序编号的废弃的电话线杆,任意两根线杆之间没有电话线连接,一共有 p(1<=p<=10000) p ( 1 <= p <= 10000 ) 对电话杆可以拉电话线。其他的由于地震使得无法连接。第i对电线杆的两个端点分别是ai,bi,它们的距离为 li(1<=li<=1000000) l i ( 1 <= l i <= 1000000 ) 。数据中每对 (ai,bi) ( a i , b i ) 只出现一次。编号为1的电话杆已经接入了全国的电话网络,整个市的电话线全都连到了编号 N N 的电话线杆上。也就是说,笨笨的任务仅仅是找一条将1号和 N N 号电线杆连起来的路径,其余的电话杆并不一定要连入电话网络。电信公司决定支援灾区免费为此市连接k对由笨笨指定的电话线杆,对于此外的那些电话线,需要为它们付费,总费用决定于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。如果需要连接的电话线杆不超过k对,那么支出为0.请你计算一下,将电话线引导震中市最少需要在电话线上花多少钱?
输入输出格式
输入格式:
输入文件的第一行包含三个数字n,p,k;
第二行到第p+1行,每行分别都为三个整数ai,bi,li。
输出格式:
一个整数,表示该项工程的最小支出,如果不可能完成则输出-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

分析

我们正着想不好想,所以我们可以二分再进行判断,我们二分最大费用,将大于的赋1,否则赋0,走1到n的最短路,若大于k,则表示费用小,使l=mid+1,否则 r=mid1 r = m i d − 1 ,这样二分出答案就行了,注意输出-1
上代码

#include<bits/stdc++.h>
using namespace std;
int tot=0,n,p,k,hed[1010],ver[200010],nxt[200010],we[200010],we2[200010],ans,dis[1010];
bool vis[1010];
void add(int x,int y,int z){
    ver[++tot]=y,nxt[tot]=hed[x],hed[x]=tot,we[tot]=z;
}
bool check(int x){
    memset(we2,0,sizeof(we2));
    for(int i=1;i<=tot;i++)
        if(we[i]>x)
            we2[i]=1;
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;
    q.push(1);
    vis[1]=1,dis[1]=0;
    while(q.size()){
        int y=q.front();
        q.pop();
        vis[y]=0;
        for(int i=hed[y];i;i=nxt[i]){
            int p=ver[i];
            if(dis[p]>dis[y]+we2[i]){
                dis[p]=dis[y]+we2[i];
                if(!vis[p])
                    vis[p]=1,q.push(p);
            }
        }
    }
    if(dis[n]<=k)
        return 1;
    return 0;
}
int main(){
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1,x,y,z;i<=p;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
    }
    int l=0,r=2000000;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid))
            ans=mid,r=mid-1;
        else
            l=mid+1;
    }
    if((ans==0)&&(r==2000000))
        ans=-1;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值