洛谷P1462 通往奥格瑞玛的道路

题目分析

题目描述

在艾泽拉斯,有n个城市。编号为1,2,3,...,n。城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

分析过程

看到最大值最小,想到二分答案

考虑到答案在固定集合过路费(val)中,我们只需要保证该集合单调性并在其中二分即可

二分点权集合,每一次都会得到一个点权,这个点权将是路径上所有点点权的最大值

在整张图上寻找路径,但是因为我们得到了一个最大值,所以在寻找过程中不选超过二分值得点

寻找路径便是招最短路,因为我们要的不能死亡就是扣血尽量小

要是不致死,那么二分出的点权就是有效的,继续向下二分,缩小点权范围
不然的话,扩大点权范围

还有一个不同于别人的优化,能加快二分速度

我们初始点在1号,结束点在n号,这两个点是必须经过的,所以最小的点权必须要比这两个点权更大,所以就可以从这两个值中间的更大的那个值的排名作为二分的左端点,详见代码

#include<bits/stdc++.h>
#define re register
#define ll long long
using namespace std;
inline int read()
{
	int k=1,sum=0;
	char c=getchar();
	for(;c<'0' || c>'9';c=getchar()) if(c=='-') k=-1;
	for(;c>='0' && c<='9';c=getchar()) sum=sum*10+c-'0';
	return sum*k;
}
int n,m,b;
struct Edge{
	int to,nxt,w;
};
const int N=1e4+10,M=5e4+10;
const int inf=1000000000+10;
Edge edge[M<<1];
int rk=1;
int head[N],cnt;
int val[N],tp[N];
int dis[N];
bool vis[N];
inline void Add(int x,int y,int w){
	edge[++cnt].to=y;edge[cnt].nxt=head[x];edge[cnt].w=w;head[x]=cnt;
}
struct New{
	int u,d;
	bool operator<(const New& qwq) const{
		return d>qwq.d;
	}
};
priority_queue<New> Q;
inline bool Check(int top){
	memset(dis,0x3f3f3f3f,sizeof(dis));memset(vis,0,sizeof(vis));
	dis[1]=0;
	Q.push((New){1,0});
	while(!Q.empty()){
		New fr=Q.top();Q.pop();
		if(vis[fr.u]) continue;
		vis[fr.u]=1;
		int x=fr.u;
		for(re int i=head[x];i;i=edge[i].nxt){
			int y=edge[i].to,z=edge[i].w;
			if(val[y]>top) continue;
			if(dis[y]>dis[x]+z)
			dis[y]=dis[x]+z;
			Q.push((New){y,dis[y]});
		}
	}
	if(dis[n]<=b) return 1;else return 0;
}
int main()
{
	//freopen("agrm.in","r",stdin);
	n=read();m=read();b=read();int js;val[1]=read();js=val[1];tp[1]=val[1];
	for(re int i=2;i<=n;++i) {
		val[i]=read();tp[i]=val[i];
		if(val[i]<js) ++rk;   //进行点权排序
	}
	for(re int i=1;i<=m;++i){
		int u=read(),v=read(),w=read();
		Add(u,v,w),Add(v,u,w);
	}
	sort(tp+1,tp+1+n); // 保证单调性,开始二分
	if(!Check(inf)) {
		puts("AFK");
		return 0;
	}
	int l=rk,r=n,ans;
	int mid;
	while(l<=r){
		mid=(l+r)>>1;
		bool pd=Check(tp[mid]);
		if(pd) {
			r=mid-1;ans=tp[mid];
		}
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}
posted @ 2019-08-29 09:41  IcedMoon_YYY  阅读(178)  评论(1编辑  收藏  举报