架设电话线

博客围绕架设电话线问题展开,该问题有n个点由P条线连接,可让任意k边权值设为0,需找出最小的最大权值边。介绍了样例输入输出及解释、数据范围,给出用二分和SPFA解决问题的思路,即二分最大长度,建邻接表并做最短路判断答案范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

架 设 电 话 线 架设电话线 线

题目链接:jzoj 2132

题目大意

n n n个点,用 P P P条线连接着,我们可以让任意 k k k边的权值设为0,我们要求出在这种情况下,最小要经过哪条权值最大的线的权值。

样例输入

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 5 5根废弃的电话线杆。电话线杆 1 1 1不能直接与电话线杆 4 4 4 5 5 5相连。电话线杆 5 5 5不能直接与电话线杆 1 1 1 3 3 3相连。其余所有电话线杆间均可拉电话线。电信公司可以免费为 F J FJ FJ连结一对电话线杆。

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

数据范围

1 &lt; = N &lt; = 1 , 000 1 &lt;= N &lt;= 1,000 1<=N<=1,000
1 &lt; = P &lt; = 10 , 000 1 &lt;= P &lt;= 10,000 1<=P<=10,000
1 &lt; = 线 的 权 值 &lt; = 1 , 000 , 000 1 &lt;= 线的权值&lt;= 1,000,000 1<=线<=1,000,000
0 &lt; = K &lt; N 0 &lt;= K &lt; N 0<=K<N

思路

这道题我们用二分和 S P F A SPFA SPFA来做。
我们二分最大长度,然后建邻接表时权值比二分值大的就把权值标为 1 1 1,否则标为 0 0 0。然后做最短路,如果无法到达就输出 − 1 -1 1,直接退出;如果点 1 1 1 n n n要用的长度大于 k k k,答案就在右边;否则就在左边。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
struct note
{
	int to,next;
}e[20001];
int n,p,k,le[1001],a[1001][1001],x[10001],y[10001],z[10001],l=2147483647,r=-2147483647,ans[20001];
bool in[1001],yes;
bool ch()
{
	memset(in,0,sizeof(in));//初始化
	for (int i=1;i<=20001;i++) ans[i]=1000000000;//初始化
	int temp=ans[1];//储存
	ans[1]=0;//初始化
	queue<int>l;
	l.push(1);
	in[1]=1;
	while (l.size())//SPFA
	{
		int now=l.front();
		l.pop();
		for (int i=le[now];i;i=e[i].next)
		if (ans[e[i].to]>ans[now]+a[now][e[i].to])
		{
			ans[e[i].to]=ans[now]+a[now][e[i].to];
			if (!in[e[i].to])
			{
				in[e[i].to]=1;
				l.push(e[i].to);
			}
		}
		in[now]=0;
	}
	if (ans[n]==temp)//不能到达
	{
		yes=1;
		printf("-1");
		return 0;
	}
	if (ans[n]>k) return 0;//在二分值的左边(比二分值小)
	return 1;//在右边(比二分值大)
}
int main()
{
//	freopen("phoneline.in","r",stdin);
//	freopen("phoneline.out","w",stdout);
	scanf("%d%d%d",&n,&p,&k);//读入
	for (int i=1;i<=p;i++)
	{
		scanf("%d%d%d",&x[i],&y[i],&z[i]);//读入
		r=max(r,z[i]);//求出二分最大值
	}
	l=0;
	int rr=r;
	while (l<r)//二分
	{
		if (yes) break;//不能到达
		int mid=(l+r)/2,kk=0;
		for (int i=0;i<=1000;i++)
		for (int j=0;j<=1000;j++)
		a[i][j]=1000000000;//初始化
		memset(e,0,sizeof(e));//初始化
		memset(le,0,sizeof(le));//初始化
		for (int i=1;i<=p;i++)
		if (z[i]<=mid)//权值比二分值小
		{
			e[++kk]=(note){x[i],le[y[i]]};le[y[i]]=kk;//建邻接表
			e[++kk]=(note){y[i],le[x[i]]};le[x[i]]=kk;//反向再建一个
			a[x[i]][y[i]]=a[y[i]][x[i]]=0;//标记
		}
		else//权值比二分值打
		{
			e[++kk]=(note){x[i],le[y[i]]};le[y[i]]=kk;//建邻接表
			e[++kk]=(note){y[i],le[x[i]]};le[x[i]]=kk;//反向再建一个
			a[x[i]][y[i]]=a[y[i]][x[i]]=1;//标记
		}
		if (ch()) r=mid;//让最大长度小于等于二分值可以
		else l=mid+1;//不可以
	}
	if (!yes) printf("%d",l);//输出二分值
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值