P1396 营救
题目链接:https://www.luogu.com.cn/problem/P1396
法一:二分+并查集
题目求经过道路的拥挤度最大值最小。我们可以考虑二分。我们就分距离。
L = (边的最小值) R = (边的最大值)
于是我们可以得到
while(l <= r)
{
mid = (l +r) >> 1;
if(check(mid)) ans = mid,r = mid - 1;
else l = mid + 1;
}
现在的问题就是check函数怎么写
我们可以这样写:如果用权值小于等于mid的边能到达t,return 1;
else return 0;
看到有人在check里跑最短路,但是蒟蒻觉得不用,只有将权值小于等于mid的边的量个点合并起来,最后再用并查集判一下
bool check(int x)
{
for(register int i = 1;i <= n; i++) fa[i] = i;
for(int i = 1;i <= m; i++) if(val[i] <= x) H_(u[i],go[i]);
//合并
return judge(a,b);
//用并查集判一下
}
【代码】
#include<bits/stdc++.h>
//#define min(x,y)(x<y?x:y)
//#define max(x,y)(x>y?x:y)
using namespace std;
int n,m,u[20005],v[20005],w[20005];
int f[10005],s,t;
int l=999999999,r,mid,ans;
int add(int n){
if(f[n]==n) return n;
return f[n]=add(f[n]);
}
//并查集
void hb(int x,int y){
if(add(x)!=add(y)) f[add(x)]=f[add(y)];
}
//合并
bool pd(int a,int b){
if(add(a)!=add(b)) return 0;
else return 1;
}
//判断两点是否连通
bool check(int x){
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++) if(w[i]<=x) hb(u[i],v[i]);
return pd(s,t);
}
int main(){
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i]>>w[i];
l=min(l,w[i]),r=max(r,w[i]);
}
// cout<<"l:"<<l<<" "<<"r:"<<r<<endl;
while(l<=r){
mid=(l+r)>>1;
// cout<<"mid:"<<mid<<endl;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}
法二:克鲁斯卡尔
将边从小到大排序,然后克鲁斯卡尔最小生成树连边,这样当S和T第一次联通时,当前边的权值就是答案了.
1 #include<bits/stdc++.h>
2 using namespace std;
3 int n,m,s,t,ans;
4 int f[10005];
5
6 struct node{
7 int u,v,w;
8 }b[20005];
9
10 int add(int n){
11 if(f[n]==0) return n;
12 return f[n]=add(f[n]);
13 }
14
15 bool cmp(node x,node y){
16 return x.w<y.w;
17 }
18
19 int main(){
20 cin>>n>>m>>s>>t;
21 for(int i=1;i<=m;i++){
22 cin>>b[i].u>>b[i].v>>b[i].w;
23 }
24 sort(b+1,b+m+1,cmp);
25 for(int i=1;i<=m;i++){
26 int xx=add(b[i].u) , yy=add(b[i].v);
27 if(xx!=yy){
28 f[xx]=yy;
29 }
30 if(add(s)==add(t)){
31 cout<<b[i].w<<endl;
32 return 0;
33 }
34 }
35 return 0;
36 }