P1462 通往奥格瑞玛的道路 题解

题目传送门

1.题外话

最近在刷有关图论,dp的题单~

2.解题意

n个节点,m条双向边。每个节点有一个权值\(f[i]\),每个边有一个边权(\(edge[i].dis\)),起点编号是1,终点编号是n。让你求对于每一个b,使得\(1到n\)的最短路小于边权和小于等于b且使得路径上经过的最大的点权最小。

3.找思路

很明显,对于“最大值最小”\(or\)“最小值最大”的问题,考虑二分;

我们二分枚举一个节点限制\(now\),点权f大于now的点不会考虑进路径,剩下的节点跑一遍最短路;

如果到终点的最短路的dis数组,也就是最小边权和小于now,说明当前的这条路径减少的血量小于当前二分的血量,存在着最多的一次收取的费用的最小值更大的可能,我们就尝试使\(r=mid-1\),将now值缩小,继续二分下去。直到我们的l和r相差为1或者0.说明我们找到了那个最小的满足条件的点权和。输出即可。

对于二分的题目,一定要明确自己要二分得到的结果,并且要清楚地知道边界情况的处理方式。如果想错了可能你的样例跑出来是对的,但是其他的一些数据会出锅。

4.\(Code\)

在这里用的是spfa。也可以用dij

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define re register
#define N 10007
#define inf 2147483646
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m,b,num,hea[N],d[N],vis[N],f[N];
int u,v,w;
struct edg{
	int next,to,dis;
}edge[N*20];
inline void add(int from,int to,int dis)
{
	num++;
	edge[num].dis=dis;
	edge[num].to=to;
	edge[num].next=hea[from];
	hea[from]=num;
}
inline bool spfa(int now)
{
	if(now<f[1])return 0;
	queue<int> q;
	for(int i=1;i<=n;i++)
		d[i]=1e9;
	memset(vis,0,sizeof(vis));
	vis[1]=1;
	d[1]=0;
	q.push(1);
	while(!q.empty())
	{
		int k=q.front();q.pop();
		vis[k]=0;
		for(int i=hea[k];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(d[v]>d[k]+edge[i].dis&&f[v]<=now)
			{
				d[v]=d[k]+edge[i].dis;
				if(!vis[v])
				{
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	if(d[n]<b)return 1;
	return 0;
}
int main()
{
	n=read(),m=read(),b=read();
	for(int i=1;i<=n;i++)
		f[i]=read();
	for(int i=1;i<=m;i++)
	{
		u=read(),v=read(),w=read();
		add(u,v,w),add(v,u,w);
	}
	int l=1,r=1e9+1;
	if(!spfa(r))
	{
		printf("AFK\n");
		return 0;
	}
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(spfa(mid))
			r=mid-1;
		else l=mid+1;
	}
	printf("%d",l);
	return 0;
}

完结撒花。有问题可以在评论区指出。

posted @ 2020-07-04 21:19  李白莘莘学子  阅读(108)  评论(0编辑  收藏  举报