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;
}
posted @ 2022-01-26 15:07  蒟蒻xiezheyuan  阅读(20)  评论(0编辑  收藏  举报