浅谈最短路应用
最短路算法除了一些基础的应用,还有一些奇奇怪怪的食用方法。
Luogu P1396 营救
先从最最最简单的题开始,这题可以利用克鲁斯卡尔最小生成树的思想做,当 \(s\) ,\(t\) 两点在加边过程中连通了,那么此时的边权就是这条路径上最小的最大边权。
但是可以考虑用bfs的解法做此题。我们不妨二分出答案要求的量,然后跑bfs,如果遇到一条边大于这个二分出来的 \(mid\),就不走这条边,如果可以从 \(s\) 走到 \(t\),则说明这种情况成立,就减小 \(mid\) 继续判断,直到最后求出答案。
代码题解有,也很好写。
Luogu P1948 Telephone Lines S
这题是很巧妙的一题,可以用最短路来解决,只不过需要改一点东西。
先考虑贪心,显然那 \(k\) 个电线杆长度要尽可能大,所以我们只需要付第 \(k+1\) 大的边的钱。
我们可以二分出第 \(k+1\) 大的边所需要的钱,记为 \(mid\)。然后我们把图中小于等于 \(mid\) 的边的边权改为 \(0\),即不需要花免费次数;把大于 \(mid\) 的边的边权改为 \(1\),即需要 \(1\) 次免费次数。最后拉去跑一边最短路,如果终点的最短路径大于 \(k\) 则不成立,\(mid\) 增大,反之成立,\(mid\) 减小,直到最后求出答案。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,INF=0x3f3f3f3f;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
int n,m,k;
int hd[N],nx[N<<1],to[N<<1],val[N<<1],tot;
void adde(int u,int v,int vol){
nx[++tot]=hd[u];to[tot]=v;val[tot]=vol;hd[u]=tot;
nx[++tot]=hd[v];to[tot]=u;val[tot]=vol;hd[v]=tot;
}
int dis[N],vis[N];
bool check(int mid){
queue<int> q;
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++){
dis[i]=INF;
vis[i]=0;
}
q.push(1),vis[1]=1,dis[1]=0;
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=hd[u];i;i=nx[i]){
int v=to[i],vol=0;
if(val[i]>mid) vol=1;
if(dis[v]>dis[u]+vol){
dis[v]=dis[u]+vol;
if(!vis[v]) q.push(v);vis[v]=1;
}
}
}
if(dis[n]>k) return false;
return true;
}
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
int x=read(),y=read(),z=read();
adde(x,y,z);
}
int l=0,r=1e6,ans=-1,mid;
while(l<=r){
mid=l+r>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d",ans);
return 0;
}
Luogu P1137 旅行计划
这道题其实和最短路没有半毛钱关系,写累了就做做这道水题。
看题,就是求以每个点为起点的最长路,又因为是有向无环图,可以考虑直接拓扑排序解决本题。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=2e5+5;
int n,m,f[N];
int hd[N],nx[N],to[N],in[N],tot;
void adde(int u,int v){
nx[++tot]=hd[u];to[tot]=v;hd[u]=tot;in[v]++;
}
void topo(){
queue<int> q;
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) if(!in[i]){q.push(i);f[i]=1;}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=hd[u];i;i=nx[i]){
int v=to[i];
in[v]--;
f[v]=max(f[v],f[u]+1);
if(!in[v]) q.push(v);
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
adde(x,y);
}
topo();
for(int i=1;i<=n;i++)
printf("%d\n",f[i]);
return 0;
}
POJ-2728 Desert King
这题可以考虑用最小生成树解决(最小比值生成树)。
颓一波式子:
众所周知,最小生成树求的是最小的 \(\sum (x_i*w_i)\quad(x_i\in \{0,1\})\) 其中 \(w_i\) 指图中各边边权。
我们不妨设这题要求的比值为 \(ans\)
由题简单可以得到这个式子(\(c_i\)为花费,\(v_i\)为价值)
移项
化简
发现这题就是
因为 \(ans\) 不能确定,所以我们可以二分出 \(ans\) 的取值,不难发现 \(ans\) 越大,生成树的边权和越小,所以如果满足上述不等式,则 \(ans\) 继续增大。
因为这是一张完全图,所以我们应该选用 \(prim\) 算法求最小生成树。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e3+5,INF=0x3f3f3f3f;
int n,vis[N];
struct QK{
double x,y,z;
}node[N];
double cost[N][N],dist[N][N],dis[N],mid;
double getdis(QK a,QK b){return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));}
bool prim(){
memset(vis,0,sizeof(vis));
double cnt=0;
for(int i=1;i<=n;i++) dis[i]=cost[1][i]-mid*dist[1][i];
vis[1]=1;
for(int LOL=1,now;LOL<=n;LOL++){
double minn=INF;
for(int i=1;i<=n;i++)
if(!vis[i]&&dis[i]<minn) minn=dis[i],now=i;
if(minn==INF) break;
cnt+=minn;
vis[now]=1;
for(int i=1;i<=n;i++)
if(!vis[i]&&dis[i]>cost[now][i]-mid*dist[now][i]) dis[i]=cost[now][i]-mid*dist[now][i];
}
if(cnt>=0) return true;
else return false;
}
int main(){
while(scanf("%d",&n),n){
for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&node[i].x,&node[i].y,&node[i].z);
double mxl=-1,mxc=-1,mil=INF,mic=INF;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
dist[j][i]=dist[i][j]=getdis(node[i],node[j]);
cost[j][i]=cost[i][j]=abs(node[i].z-node[j].z);
mxl=max(mxl,dist[i][j]);
mil=min(mil,dist[i][j]);
mxc=max(mxc,cost[i][j]);
mic=min(mic,cost[i][j]);
}
}
double l=mic/mxl,r=mxc/mil;
while(r-l>1e-4){
mid=(l+r)/2;
if(prim()) l=mid;
else r=mid;
}
printf("%.3lf\n",l);
}
return 0;
}

浙公网安备 33010602011771号