D-逃亡的贝贝_牛客练习赛104 (nowcoder.com)
题意:给你一个n个点,m条双向边的图(有边权),再给你起点s与终点t,以及有k个药水可以使某一条边,减小,求起点到终点经历边权最小值为多少.
题解:
首先建图,然后看到题中让我们找的是一个最小,或最大的值,是一个线性的值,我们就可以想到用二分来解决,但在遍历途中,还要记得用上双端队列,后面会说为什么
然后,我们开始来遍历图,从起点开始遍历,
取元素都从队头取
遇到小于我们二分出的边权x的,
dis[j] = min(dis[j].dis[tem])
q.push_front(j)
否则如果大于x,用上药水如果小于dis[j] = min(dis[j],dis[tem]+1)
q.push_back(j)
如果小于,说明他就是当前最优路径,不需要再从头循环,否则会改变,
如果大于,从头遍历,看是否有更优情况
遍历到t时,return dis[t] <= k
看是否能在k瓶药水内到达终点,
如果遍历不到t,return 0;
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,s,t,k;
const int N = (2e5+10)*2;
const int M = 100050;
int h[M],e[N],ne[N],w[N],idx;
bool st[M];
int dis[M];
void add(int l,int r,int x)
{
w[idx] = x;
e[idx] = r;
ne[idx] = h[l];
h[l] = idx++;
}
int solve(int x)
{
memset(st,0,sizeof st);
memset(dis,0x3f,sizeof dis);
deque<int> q;
q.push_front(s);
dis[s] = 0;
while(q.size())
{
int tem = q.front();
q.pop_front();
if(tem == t)
{
return dis[t] <= k;
}
if(st[tem])
continue;
st[tem] = 1;
for(int i = h[tem];i != -1;i = ne[i])
{
int j = e[i];
if(w[i] <= x)
{
dis[j] = min(dis[j],dis[tem]);
q.push_front(j);
}
else
{
if(((long long)114*w[i]+513)/(long long)514 <= x)
{
dis[j] = min(dis[j],dis[tem]+1);
q.push_back(j);
}
}
}
}
return 0;
}
int main()
{
memset(h,-1,sizeof h);
cin >> n >>m>>s>>t>>k;
for(int i = 1;i <= m;i++)
{
int l,r,x;
cin >>l >>r>>x;
add(l,r,x);
add(r,l,x);
}
int l = 0,r = 1e9;
while(l <= r)
{
int mid = (l+r)/2;
if(solve(mid))
{
r = mid -1;
}
else
{
l = mid + 1;
}
}
if(l > 1e9)
{
cout<<"I really need TS1's time machine again!";
}
else
{
cout<<l;
}
}