[LuoguP1462]通往奥格瑞玛的道路
题意简述:现在有一个图,每经过一个点就会交钱,走一条路就会扣血。在血量>0的前提下,要从1走到n点,并且要求路径上交钱的最大值最小。
解题思路:首先最大值最小,我们选择二分。目前有两个限制条件,血量与金钱。我们选择二分金钱,因为二分金钱就可以将一部分的城市排除在外,但二分血量不行(扣血是累加的,而金钱只是单个点,所以删点比删边更好了)。那么二分了金钱之后限制了部分城市不能走,二分对应的check就用dijistra,跑一遍可以去的城市中的最短路(路上扣血作为边权),此时我们可以求得扣血最少,再与总血量进行比较,比总血量少就是一个可行解,继续二分看一看是否有更优的答案。 至于没有解则是不对城市进行限制都无法活着回去。
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<fstream> #include<queue> using namespace std; int read(){ int res=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ res=res*10+(ch-'0'); ch=getchar(); } return res*f; } const int MAXN=100005; int n,m,b,f[MAXN],head[MAXN],ne[MAXN],to[MAXN]; int c[MAXN],tot,money,vis[MAXN],ans; priority_queue <pair<int,int> > q; long long d[MAXN]; void add(int x,int y,int u){ to[++tot]=y; c[tot]=u; ne[tot]=head[x]; head[x]=tot; } bool dijistra(int max_money){ memset(d,127/3,sizeof(d)); memset(vis,0,sizeof(vis)); d[1]=0;vis[1]=1; q.push(make_pair(0,1)); while(!q.empty()){ int u=q.top().second; q.pop(); //vis[u]=0; for(int i=head[u];i;i=ne[i]){ int v=to[i]; if(f[v]>max_money||f[u]>max_money)continue; if(d[v]>d[u]+c[i]){ d[v]=d[u]+c[i]; if(!vis[v]){ vis[v]=1; q.push(make_pair(-d[v],v)); } } } } if(d[n]<b)return 1; else return 0; } int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); n=read();m=read();b=read(); int l,r; for(int i=1;i<=n;++i){ f[i]=read(); r=max(f[i],r); } l=0; int a,b,c; for(int i=1;i<=m;++i){ a=read();b=read();c=read(); add(a,b,c); add(b,a,c); } if(!dijistra(1<<30)){ printf("AFK"); return 0; } r++; while(l<r){ int mid=(l+r)>>1; if(dijistra(mid)){ r=mid; } else { l=mid+1; } } printf("%d",l); return 0; }