收费站

2 篇文章 0 订阅
2 篇文章 0 订阅
收费站

【题目描述】
在某个遥远的国家里,有n个城市。编号为1,2,3,……,n。
这个国家的政府修建了m条双向的公路。每条公路连接着两个城市。沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。
开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。所有的收费站都在城市中,在城市间的公路上没有任何的收费站。
小红现在要开车从城市u到城市v(1<=u,v<=n)。她的车最多可以装下s升的汽油。在出发的时候,车的油箱是满的,并且她在路上不想加油。

在路上,每经过一个城市,她要交一定的费用。如果她某次交的费用比较多,她的心情就会变得很糟。所以她想知道,在她能到达目的地的前提下,她交的费用中最多的一次最少是多少。这个问题对于她来说太难了,于是她找到了聪明的你,你能帮帮她吗?


【输入格式】
第一行5个正整数,n,m,u,v,s。分别表示有n个城市,m条公路,从城市u到城市v,车的油箱的容量为s升。
接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。

再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=n)。表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,需要用ci升汽油。


【输出格式】

仅一个整数,表示小红交费最多的一次的最小值。

如果她无法到达城市v,输出-1。


【输入样例1】
4 4 2 3 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
【输出样例1】

8


【输入样例2】
4 4 2 3 3
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
【输出样例2】

-1


【数据规模】
对于60%的数据,满足n≤200,m≤10000,s≤200
对于100%的数据,满足n≤10000,m≤50000,s≤1000000000

对于100%的数据,满足ci≤1000000000,fi≤1000000000,可能有两条边连接着相同的城市。


分析:这是一道很经典的二分+最短路的题目,先看收费站金额和边权,如果从以上两者入手基本上就是二分的思路。具体思路如下:首先将所有的收费价格从小到大排序,将价格数组进行二分,而后用汽车耗油跑最短路,跑最短路时若要去的城市u的收费大于你当前划定的价格,那就不能去。最后用到终点的最小耗油量与油箱油量比较,小于等于返回true,否则返回false。


#include<cstdio>
#include<queue>
#include<map>
#include<cstring>
#include<iostream>
#include<algorithm>
#define name "cost"
using namespace std;
const int MAXN = 10001,MAXM = 50001;
int n,m,st,en,oil,save[MAXN],cnt,cost[MAXM],fei[MAXN];
int minx[MAXN];
struct edgeb{
	int to,next,v;
}edge[MAXM*2];
map <int,bool> judge;
void add(int x,int y,int v)
{
	edge[++cnt].to=y;
	edge[cnt].next=save[x];
	edge[cnt].v=v;
	save[x]=cnt;
}
bool cmp(int a,int b)
{
	return a<b;
}
bool find(int lim)
{
	if(fei[st]>lim||fei[en]>lim)return 0;
	memset(minx,0x7f,sizeof(int)*(n+5));
	queue <int> d;
	minx[st]=0;
	d.push(st);
	while(!d.empty())
	{
		int x=d.front();
		d.pop();
		for(int i=save[x];i;i=edge[i].next)
		{
			int y=edge[i].to;
			if(fei[y]<=lim&&minx[y]>minx[x]+edge[i].v)
			{
				minx[y]=minx[x]+edge[i].v;
				d.push(y);
			}
		}
	}
	if(minx[en]<=oil)return 1;
	else return 0; 
}
int main()
{
	freopen(name ".in","r",stdin);
	freopen(name ".out","w",stdout);
	cin>>n>>m>>st>>en>>oil;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&fei[i]);
		if(!judge[fei[i]]){
			judge[fei[i]]=1;
			cost[++cost[0]]=fei[i];
			}
	}
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	sort(cost+1,cost+1+cost[0],cmp);
	int l=1,r=cost[0];
	if(!find(cost[cost[0]])){
		cout<<"-1"<<endl;
		return 0;
	}
	while(l<r)
	{
		int m = ((l+r)>>1);
		if(find(cost[m]))r=m;
		else l=m+1;
	}
	cout<<cost[l]<<endl;
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值