【二分dfs / 生成树 / SPFA】P1396 营救
P1396 营救
一、SPFA
显然的,从s到t拥挤度最小的最大值应该是经由x到t,值为3。
既然要松驰,第一时间想到最短路算法。
但本题显然不是取最短路。设dis[t]=4,显然中转算法应该是:
min(4,max(3,2)),因此只需要修改SPFA的松驰代码为:
if(dis[v]>max(dis[u],w[x]) dis[v]=max(dis[u],w[x]) 即可。
#include <bits/stdc++.h>
using namespace std;
struct edge {
int u,v,w,next;
} e[50005];
int vex[10005],dis[10005],vis[10005],que[50005],head=0,rear=0;
int n,m,s,t;
int spfa(int s,int t){
for(int i=1;i<=n;i++) dis[i]=1e9;
dis[s]=0;
que[rear++]=s;
while(head<rear){
vis[que[head]]=0;
for(int i=vex[que[head]];i;i=e[i].next){
int d=max(dis[que[head]],e[i].w);//关键代码
if(d<dis[e[i].v]){//关键代码
dis[e[i].v]=d;
if(!vis[e[i].v]){
que[rear++]=e[i].v;
vis[e[i].v]=1;
}
}
}
head++;
}
return dis[t];
}
int main(){
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
cin>>e[i].u>>e[i].v>>e[i].w;
e[i].next=vex[e[i].u];
vex[e[i].u]=i;
e[m+i].u=e[i].v;
e[m+i].v=e[i].u;
e[m+i].w=e[i].w;
e[m+i].next=vex[e[i].v];
vex[e[i].v]=i+m;
}
cout<<spfa(s,t);
return 0;
}
二、最小生成树
换种思维,要使拥挤度较小,我们优先选取最小边,逐渐增大,直到s和t连通,这不是最小生成树吗?
#include <bits/stdc++.h>
using namespace std;
struct edge {
int u,v,w;
} e[50005];
int pre[10005],k,n;
int cmp(edge a,edge b){
return a.w<b.w;
}
void add(int u,int v,int w){
k++;
e[k].u=u;
e[k].v=v;
e[k].w=w;
return;
}
int Find(int x){
if(pre[x]==x) return x;
return pre[x]=Find(pre[x]);
}
int mst(int s,int t){
for(int i=1;i<=n;i++) pre[i]=i;
sort(e+1,e+k+1,cmp);
for(int i=1;i<=k;i++){
int u=Find(e[i].u),v=Find(e[i].v);
if(u!=v){
pre[u]=v;
//s和t连通,可以中止了,此边就是最大边
if(Find(s)==Find(t)) return e[i].w;
}
}
return -1;
}
int main(){
int u,v,w,m,s,t;
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
add(u,v,w);
}
cout<<mst(s,t);
return 0;
}
三、二分+dfs
最小的最大值,二分!我们猜一个答案,比答案大的边不能走,如果还能走到终点,说明答案大了,否则答案小了。
#include <bits/stdc++.h>
using namespace std;
struct edge {
int u,v,w,next;
} e[50005];
int k,n,f,vis[10005],vex[10005],s,t;
void add(int u,int v,int w) {
k++;
e[k].u=u;
e[k].v=v;
e[k].w=w;
e[k].next=vex[u];
vex[u]=k;
return;
}
void dfs(int u,int mid) {
if(vis[u]||f) return;
vis[u]=1;
if(u==t) { //到终点,标记,强制中止dfs
f=1;
return;
}
for(int i=vex[u]; i; i=e[i].next) {
//比mid大的边不能走
if(e[i].w<=mid) dfs(e[i].v,mid);
}
return;
}
int check(int mid) {
f=0;
memset(vis,0,sizeof(vis));
dfs(s,mid);
return !f;
}
int main() {
int u,v,w,m;
cin>>n>>m>>s>>t;
for(int i=1; i<=m; i++) {
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
int l=1,r=10000,mid;
while(l<=r) {
mid=(l+r)/2;
if(check(mid)) l=mid+1;
else r=mid-1;
}
cout<<l;
return 0;
}