P1462 通往奥格瑞玛的道路
题面
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。
有一天他醒来后发现自己居然到了联盟的主城暴风城。
在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。
在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\)。
城市之间有 \(m\) 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。
每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设 \(1\) 为暴风城,\(n\) 为奥格瑞玛,而他的血量最多为 \(b\),出发时他的血量是满的。
歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。
看看标签
二分答案暗示了我们这道题需要用到二分。
我们可以二分题面中的歪嘴哦交费最多的一次的最小值。
那熟悉的\(check\)函数呢?标签说了要用SPFA,那就跑SPFA,求从暴风城到奥格瑞玛的最小损血是否大于血量就完了。
那什么时候输出AFK
呢?当然是在无穷多的血量也无法到达的时候啦!
贴上代码
先奉上二分模板供大家参考
int l=1,r=n;
int ans;
while(l<=r){
int mid=(l+r)/2;
if(check(mid)==true){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
最终代码如下所示:
#include<bits/stdc++.h>
using namespace std;
const int SIZE = 10010;
const int ESIZE = 100010;
const int inf=0x3f3f3f3f;
struct e{
int next,to,w;
}edge[ESIZE];
int cnt;
int head[SIZE];
int add(int a,int b,int c){
cnt++;
edge[cnt].to=b;
edge[cnt].w=c;
edge[cnt].next=head[a];
head[a]=cnt;
cnt++;
edge[cnt].to=a;
edge[cnt].w=c;
edge[cnt].next=head[b];
head[b]=cnt;
}
int n,m,b;
int f[SIZE];
int u[SIZE];
int dist[SIZE];
bool v[SIZE];
bool spfa(int top){
memset(dist,inf,sizeof(dist));
memset(v,0,sizeof(v));
queue<int> q;
v[1]=true;dist[1]=0;
q.push(1);
while(!q.empty()){
int temp=q.front();q.pop();
v[temp]=false;
for(int i=head[temp];i!=0;i=edge[i].next){
int to=edge[i].to;
int wr=edge[i].w;
if(dist[to]>dist[temp]+wr&&f[to]<=top){
dist[to]=dist[temp]+wr;
if(v[to]==false) {
v[to]=true;
q.push(to);
}
}
}
}
if(dist[n]<=b) {
return true;
}
else{
return false;
}
}
bool check(int top){
return spfa(u[top]);
}
int main(){
cin>>n>>m>>b;
for(int i=1;i<=n;i++){
cin>>f[i];
u[i]=f[i];
}
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
sort(u+1,u+n+1);
if(!spfa(inf)){
cout<<"AFK"<<endl;
exit(0);
}
int l=1,r=n;
int ans;
while(l<=r){
int mid=(l+r)/2;
if(check(mid)==true){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
cout<<u[ans]<<endl;
return 0;
}