【复赛模拟试题】收费站

【问题描述】  
  
  在某个遥远的国家里,有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


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


 
    
 【输出样例】  
   
【样例1】
8


【样例2】
-1


 
    
 【数据范围】  
   

对于60%的数据,满足n<=200,m<=10000,s<=200

对于100%的数据,满足n<=10000,m<=50000,s<=1000000000

对于100%的数据,满足ci<=1000000000,fi<=1000000000,可能有两条边连接着相同的城市。(就是有重边)

思路:根据题目给出的数据我们不难看出这是一道图论题(凑字数),题目要求的是最大值最小,对于这类型的问题,通常都有一种典型的处理方法——二分答案。

        因为我们并不需要直接去求得最大值,而是要求在所有u到v的路径中选择一个最大点权的最小值,所以我们可以猜这个最小值,然后把所有点权大于这个最小值的点舍去(因为如果这一个点的点权大于了所猜的答案就会使得u到v的一条路径上有一个点的点权大于所猜的答案),然后通过一次求最短路(dijsktra或spfa)来判断u到v的距离dist[v]是不是小于等于s来判断所猜的

 答案是否可行,如果可行,就往小的方向猜,否则往大的方向猜,最后输出答案就行了。

值得注意的是,本题边数和点数都较多,为稠密图,而spfa针对稠密图的效率并不是很高,很可能会卡数据,建议是用更为稳定的dijsktra算法。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<cstdlib>
#include<queue>
#define maxn 10005
#define inf 2000000010
using namespace std;
int n,m,u,v,S;
int f[maxn];
vector<int>g[maxn],w[maxn];
int dist[maxn];

/*bool check(int s,int mid)//spfa算法求最短路 
{
	for(int i=1;i<=n;i++)dist[i]=inf;
	queue<int>q;
	q.push(s);
	inq[s]=1;
	dist[s]=0;
	
	while(!q.empty())
	{
		int i=q.front();q.pop();
		inq[i]=0;
		
		if(f[i]>mid)continue;
		 
		for(int k=0;k<g[i].size();k++)
		{
			int j=g[i][k],c=w[i][k];
			
			if(f[j]>mid)continue;
			
			if(dist[j]>dist[i]+c)
			{
				dist[j]=dist[i]+c;
				if(inq[j])continue;
				inq[j]=1;
				q.push(j);		
			}		
		}
	}
	
	if(dist[v]<=S)return 1;
	return 0;
}*/

struct data
{
	int d,id;
	friend bool operator <(data a,data b)
	{
		return a.d>b.d;
	}
};
bool check(int s,int mid)//dijkstra算法求最短路 
{
	for(int i=1;i<=n;i++)dist[i]=inf;
	priority_queue<data>pq;
	pq.push((data){0,s});
	dist[s]=0;
	
	while(!pq.empty())
	{
		data t=pq.top();pq.pop();
		int i=t.id;
		if(f[i]>mid)continue;//如果取出的点不存在(假删除),就不要扩展 
		if(t.d>dist[i])continue;
		for(int k=0;k<g[i].size();k++)
		{
			int j=g[i][k],c=w[i][k];
			if(f[j]>mid)continue;//扩展到的点不存在就不要进入队列 
			if(dist[j]>dist[i]+c)
			{
				dist[j]=dist[i]+c;
				pq.push((data){dist[j],j});
			}
		}
	}
	
	if(dist[v]<=S)return 1;//可以到达v就往小的方向猜 
	return 0;
}

void solve()//二分猜答案 
{
	int A=0,B=1000000000,ans=-1;//如果ans的值没有改变显然就是无解 
	while(A<=B)
	{
		int mid=A+B>>1;
		if(check(u,mid))ans=mid,B=mid-1;
		else A=mid+1;
	}
	
	printf("%d",ans);
}

int main()
{
	//freopen("toll.in","r",stdin);
	//freopen("toll.out","w",stdout);
	
	scanf("%d%d%d%d%d",&n,&m,&u,&v,&S);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&f[i]);
	}
	
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		g[a].push_back(b);
		g[b].push_back(a);
		w[a].push_back(c);
		w[b].push_back(c);
	}//输入数据并建立图结构 
	
	solve();//二分答案解决问题 
	
	return 0;
} 


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值