架设电话线
题目描述
给出一张n个节点、m条边的加权无向图,求一条从1到n的路径,使第k+1大的边权尽可能小。
思路
因为这个答案显然具有单调性,我们考虑对于这个答案进行二分,接下来再想如何确定这个值是否为所求。对于所有大于mid的边,我们把它的边权设为0,把所有小于等于mid的边的边权设为1,那么对于这张图,我们求一次最短路,得到的1~n的最短路径的值就是这个值再所求路径中的排名。或者换一种说法,对于小于等于mid的边权我们需要使用一次免费机会,看到n节点最少要用几次免费机会。
代码
#include <bits/stdc++.h> using namespace std; struct Edge { int x,y,w; }e[2005]; int nxt[4005],head[2005],to[4005],w[4005],dis[1005]; int tot,n,m,p,k; bool exist[1005],f; void add_edge(int x,int y,int v) { nxt[++tot]=head[x]; head[x]=tot; to[tot]=y; w[tot]=v; } int spfa() { memset(dis,0x3f,sizeof(dis)); memset(exist,0,sizeof(exist)); queue<int>q; q.push(1);exist[1]=1;dis[1]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(dis[v]>dis[u]+w[i]) { dis[v]=dis[u]+w[i]; if(!exist[v]) { q.push(v); exist[v]=1; } } } exist[u]=0; } if(dis[n]==0x3f3f3f3f)f=1; return dis[n]; } bool check(int m) { memset(head,0,sizeof(head)); tot=0; for(int i=1;i<=p;i++) { int f=e[i].w<=m?0:1; // cout<<i<<' '<<f<<endl; add_edge(e[i].x,e[i].y,f); add_edge(e[i].y,e[i].x,f); } return spfa()<=k; } int main() { int maxx=0; scanf("%d%d%d",&n,&p,&k); for(int i=1;i<=p;i++) { scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w); maxx=max(maxx,e[i].w); } int l=0,r=maxx; while(l<r) { if(f)break ; int mid=l+r>>1; if(check(mid))r=mid; else l=mid+1; } if(!f)printf("%d",r); else printf("-1"); return 0; }