BZOJ [Usaco2007 Jan]Telephone Lines架设电话线(POJ 3662)

题目描述

Farmer John打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务。于是,FJ必须为此向电信公司支付一定的费用。

    FJ 的农场周围分布着 N(1 <= N <= 1,000) 根按 1..N 顺次编号的废弃的电话线杆,任意两根电话线杆间都没有电话线相连。一共 P(1 <= P <= 10,000) 对电话线杆间可以拉电话线,其余的那些由于隔得太远而无法被连接。
    i 对电话线杆的两个端点分别为 A_i B_i ,它们间的距离为 L_i (1 <= L_i <= 1,000,000) 。数据中保证每对 {A_i B_i} 最多只出现 1 次。编号为 1 的电话线杆已经接入了全国的电话网络,整个农场的电话线全都连到了编号为 N 的电话线杆上。也就是说, FJ 的任务仅仅是找一条将 1 号和 N 号电话线杆连起来的路径,其余的电话线杆并不一定要连入电话网络。
    经过谈判,电信公司最终同意免费为 FJ 连结 K(0 <= K < N) 对由 FJ 指定的电话线杆。对于此外的那些电话线, FJ 需要为它们付的费用,等于其中最长的电话线的长度(每根电话线仅连结一对电话线杆)。如果需要连结的电话线杆不超过 K 对,那么 FJ 的总支出为 0
    请你计算一下, FJ 最少需要在电话线上花多少钱。

输入

* 1 : 3 个用空格隔开的整数: N P ,以及 K
* 2..P+1 : i+1 行为 3 个用空格隔开的整数: A_i B_i L_i

输出

* 1 : 输出 1 个整数,为 FJ 在这项工程上的最小支出。如果任务不可能完成,输出 -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

提示

输入说明:

    一共有 5 根废弃的电话线杆。电话线杆 1 不能直接与电话线杆 4 5 相连。电话线杆 5 不能直接与电话线杆 1 3 相连。其余所有电话线杆间均可拉电话线。电信公司可以免费为 FJ 连结一对电话线杆。



输出说明:

    FJ 选择如下的连结方案: 1->3 3->2 2->5 ,这 3 对电话线杆间需要的 电话线的长度分别为 4 3 9 FJ 让电信公司提供那条长度为 9 的电话线,于是,他所需要购买的电话线的最大长度为 4

题解

二分+最短路。

首先,题目的意思就是要求一条从1到n的路,使路上第k+1大的那段最小。

二分答案路径的长度,在判定是否可行时,只需要判断是否能寻找到一条路径,使得该路径上大于我们二分的这个值的边不超过k条,实质上就是最短路做的一个变形而已,小于二分的值的边可以看做边权为0,大于的可以看做边权为1,直接求最短路看是否小于k即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
int n,m,k,zz,head[1002];
struct bian
{int to,nx,v;} e[20005];
int dis[1002],ans;
void insert(int x,int y,int z)
{
	zz++; e[zz].to=y; e[zz].v=z; e[zz].nx=head[x]; head[x]=zz;
	zz++; e[zz].to=x; e[zz].v=z; e[zz].nx=head[y]; head[y]=zz;
}
bool spfa(int x)
{
	int q[1002]; bool pd[1002];
	memset(dis,127/3,sizeof(dis));
	memset(pd,false,sizeof(pd));
	int t=0,w=1,s;
	dis[1]=0; q[t]=1; pd[1]=true;
	while(t!=w)
	   {int p=q[t],i; 
	    i=head[p],t=(t+1)%1001;
	    while(i)
		   {if(e[i].v>x) s=dis[p]+1;
		    else s=dis[p];
		    if(dis[e[i].to]>s)
		       {dis[e[i].to]=s;
			    if(!pd[e[i].to])
				   {q[w]=e[i].to; w=(w+1)%1001; pd[e[i].to]=true;}
			   }
			i=e[i].nx;
		   }
		pd[p]=false;
	   }
	if(dis[n]<=k) return true;
	else return false;
}
void erf()
{
	int mid,l=0,r=1000000;
	while(l<=r)
	   {mid=(l+r)>>1;
	    if(spfa(mid)) {ans=mid; r=mid-1;}
	    else l=mid+1;
	   }
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	   {int x,y,z;
	    scanf("%d%d%d",&x,&y,&z);
	    insert(x,y,z);
	   }
	erf();
	printf("%d",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值