​英灵召唤​​​ 我们先来看看二分答案有什么特征。
首先,既然是二分答案,那么我们的答案通常是一个数值(也可能有其他情况吧,如果有,麻烦各位指出,当然我说的一个数值并不是题目要求的数值的数量,而是我们二分的目标),这样我们才可以称得上是二分答案
但是不是所有的这样的目标都可以进行二分。
我们来看一下二分模板:

while(l+1<r){
int mid=(l+r)/2;
if(judge(mid)){
r=mid;
}else{
l=mid+1;
}
}

我们应该可以迅速发现一个核心的函数judge
这个函数是用来判断当前这个数字是否符合我们题目的要求,而这个也的确是我们的核心。
也就是说,我们二分出的答案,一定要是可判断的,也就是说我们能找到明确的方法去判断是否符合题目,这也就是我们决定使用二分答案的一个重要判断依据了。(什么复杂度之类常规的我就不说了)
所以二分答案总结如下:

  • 单次二分对象仅为一个数据
  • 有明确的方法去判断答案是否符合题目

那么接下来这道题还有一个精华,就是最短路。
这显然是一个单源最短路,而且这个复杂度,Floyd应该不会有人考虑的。
那么dijkstrta呢?堆优化好像只能过两个点,除非自己手写堆。
所以最后我们来到了SPFA(就是队列优化的Bellman-Ford)
最短路我们明天再细讲。
代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=10005;
struct edge{
int u,v,w;
edge(int u,int v,int d):u(u),v(v),w(d){}
};
vector<edge> e;
vector<int> hn[maxn];
int n,m,b;
int cost[maxn];
int mcur;
int inq[maxn];
int d[maxn];
int vis[maxn];
void addEdge(int u,int v,int w){
e.push_back(edge(u,v,w));
hn[u].push_back(mcur);
hn[v].push_back(mcur);
mcur++;
}
int work(int c){
if(cost[1]>c){
return 0;
}
queue<int> Q;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
memset(inq,0,sizeof(inq));
d[1]=0;
Q.push(1);
inq[1]=1;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=0;
vis[u]=1;
for(int i=0;i<hn[u].size();i++){
edge cure=e[hn[u][i]];
int v;
if(u!=cure.u){
v=cure.u;
}else{
v=cure.v;
}
if(cost[v]<=c&&d[v]>d[u]+cure.w){
d[v]=d[u]+cure.w;
if(inq[v]==0){
Q.push(v);
inq[v]=1;
}

}
}
}
return d[n]<=b?1:0;
}
int main(){
scanf("%d%d%d",&n,&m,&b);
int l=0x3f3f3f3f,r=0;
for(int i=1;i<=n;i++){
scanf("%d",&cost[i]);
l=min(l,cost[i]);
r=max(r,cost[i]);
}
mcur=0;
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
addEdge(a,b,c);
}

while(l+1<r){
int mid=(l+r)/2;
if(work(mid)){
r=mid;
}else{
l=mid+1;
}
}
if(work(r)){
printf("%d",r);
}else if(work(l)){
printf("%d",l);
}else{
printf("AFK");
}
return 0;
}