数据结构有些题很难,刷点图论题放松一下
题目大意:
在无向图上求出一条1~N的路径,使路径上第K+1大的边权最小,输出这个边权。
有两种方法:
1、二分答案+双向bfs
2、dp+spfa(分层图)
我们知道,适用二分搜索的前提是答案具有单调性,本题符合该条件,于是我们可以把答案转化成:是否存在一种合法的升级方法,使花费不超过mid。
转化后我们仍需做一些改变,我们令价格大于mid的边,边权看为1,小于等于的看作0,于是我们只需求1~n的最短路是否超过k即可。这里跑最短路函数我们可以采用双端队列BFS方法实现,效率为O(N),算法最终效率为O((N+P)log MAX_L)
需要注意的是二分搜索的边界条件,如何如何根据check返回值来收缩l和r,以及如何判断无解。
双端队列BFS原理
只有0、1边的图中跑最短路,可以用双向bfs以O(N)的复杂度完成,用到deque。
const int maxn=5e4+7;
int n,m,k,cnt,d[maxn],head[maxn];
bool vis[maxn];
struct edge{ int to,w,next; }e[maxn<<2];
void add(int u,int v,int w){
e[++cnt].w=w; e[cnt].to=v;
e[cnt].next=head[u]; head[u]=cnt;
}
bool cc(int mid){
memset(d,inf,sizeof(d)); memset(vis,0,sizeof(vis));
deque<int>q; q.push_back(1); d[1]=0;
while(!q.empty()){
int x=q.front(); q.pop_front();
if(vis[x]) continue; vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
int z=e[i].w>mid?1:0;
d[y]=min(d[y],d[x]+z);
if(z) q.push_back(y);
else q.push_front(y);
}
}
return d[n]<=k;
}
int main(){
n=read(); m=read(); k=read();
while(m--){
int x,y,z; x=read(); y=read(); z=read();
add(x,y,z); add(y,x,z);
}
int l=0,r=inf;
while(l<r){
int mid=l+r>>1;
if(cc(mid)) r=mid;
else l=mid+1;
}
if(l>=inf) printf("%d",-1);
else printf("%d",l);
}
分层图做法可以参考这篇博客:【NOIP2009】最优贸易(分层图,或双向最短路)
因为k在1000内,最多建1000个图,空间够,就是有点大,分享这个解法的博客:POJ 3662 Telephone Lines (分层图)