(Luogu) P1462 通往奥格瑞玛的道路(二分 + Spfa/Dijkstra)

传送门

题意:一个联通的无向图,通过每一个点会收费,通过每一条边会减少血量。求能到达终点 (即血量到终点还不为0) 的各个路径上,收费最高的最小值。

思路:首先一开始的思路是找到所有能到终点的路径,然后取每条路径上的最高收费,比较取最小值,数据量大,pass;再思考,他求的是最大的最小值,那我们可以想到二分,去二分路径上的最高消费,然后判断这样能不能走到终点。判断的函数就可以考虑Spfa和Dijkstra了,只要将这两个在跑的时候略加改变即可,如果通向的那个点,最高消费大于你枚举的最高消费,就不做考虑。最后pd一下到终点的需要消耗的最小血量是不是大于你的血量b。

这个题在同样的读入方式下Spfa是跑的比Dijkstra快的;

 

Spfa:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int inf=-0x3f3f3f3f;
const int maxn=1e5+5;
int dist[maxn];
bool vis[maxn];
int cost[maxn];
int n,m,b;
struct edge {
	int to;
	int w;
};
vector<edge> ee[maxn];

bool spfa(int maxmoney) {
	memset(dist,0x3f,sizeof(dist));
	memset(vis,false,sizeof(vis));
	dist[1]=0;
	vis[1]=true;
	queue<int> q;
	q.push(1);
	while(!q.empty()) {
		int np=q.front();
		q.pop();
		vis[np]=false;
		for(int i=0; i<(int)ee[np].size(); ++i) {
			edge tt=ee[np][i];
			if(cost[tt.to]<=maxmoney) {
				if(dist[tt.to]>dist[np]+tt.w) {
					dist[tt.to]=dist[np]+tt.w;
					if(vis[tt.to]==false) {
						q.push(tt.to);
						vis[tt.to]=true;
					}
				}
			}
		}
	}

	if(dist[n]>=b)	return false;
	else	return true;
}

int main() {
	cin>>n>>m>>b;
	int le=INF,ri=inf;
	for(int i=1;i<=n;++i){
		cin>>cost[i];
		le=min(le,cost[i]);
		ri=max(ri,cost[i]);
	}
	int x,y,z;
	for(int i=1;i<=m;++i){
		cin>>x>>y>>z;
		ee[x].push_back(edge{y,z});
		ee[y].push_back(edge{x,z});
	}

	if(!spfa(ri)){
		cout<<"AFK"<<endl;
	}
	else{
		while(le<ri){
			int mid=(le+ri)>>1;
			if(spfa(mid)){//可达
				ri=mid;
			}
			else{//不可达
				le=mid+1;
			}
		}
		cout<<le<<endl;
	}
	return 0;
}

 Dijkstra:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int inf=-0x3f3f3f3f;
const int maxn=1e5+5;
int dist[maxn];
bool vis[maxn];
int cost[maxn];
int n,m,b;
struct node {
	ll p; //当前点
	ll dis; //起始点到该点的最短距离
	node(ll np=0,ll ndis=0) {
		p=np,dis=ndis;
	}
	bool operator <(const node& x)const {
		return dis>x.dis;
	}
};

struct edge {
	int to;
	int w;
};
vector<edge> ee[maxn];

bool Dijkstra(int maxmoney) {
	memset(dist, 0x3f, sizeof(dist));
	memset(vis, false, sizeof(vis));
	dist[1]=0;
	priority_queue<node> q;
	q.push(node(1,0));

	while(!q.empty()) {
		node tmp=q.top();
		q.pop();
		int np=tmp.p;
		if(vis[np])
			continue;
		else {
			vis[np]=1;
			for(int i=0; i<ee[np].size(); ++i) {
				edge ne=ee[np][i];
				ll nto=ne.to;
				ll nw=ne.w;
				if(cost[nto]<=maxmoney) {
					if(dist[nto]>dist[np]+nw) {
						dist[nto]=dist[np]+nw;
						q.push(node(nto,dist[nto]));
					}
				}

			}
		}
	}
	if(dist[n]>=b)	return false;
	else	return true;

}

int main() {
	cin>>n>>m>>b;
	int le=INF,ri=inf;
	for(int i=1; i<=n; ++i) {
		cin>>cost[i];
		le=min(le,cost[i]);
		ri=max(ri,cost[i]);
	}
	int x,y,z;
	for(int i=1; i<=m; ++i) {
		cin>>x>>y>>z;
		ee[x].push_back(edge {y,z});
		ee[y].push_back(edge {x,z});
	}

	if(!Dijkstra(ri)) {
		cout<<"AFK"<<endl;
	} else {
		while(le<ri) {
			int mid=(le+ri)>>1;
			if(Dijkstra(mid)) {//可达
				ri=mid;
			} else { //不可达
				le=mid+1;
			}
		}
		cout<<le<<endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值