架 设 电 话 线 架设电话线 架设电话线
题目链接: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
<
=
N
<
=
1
,
000
1 <= N <= 1,000
1<=N<=1,000
1
<
=
P
<
=
10
,
000
1 <= P <= 10,000
1<=P<=10,000
1
<
=
线
的
权
值
<
=
1
,
000
,
000
1 <= 线的权值<= 1,000,000
1<=线的权值<=1,000,000
0
<
=
K
<
N
0 <= K < 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;
}