Codevs4175 收费站(重庆一中高2018级信息学竞赛测验9) 解题报告

【问题描述】  
  
  在某个遥远的国家里,有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,可能有两条边连接着相同的城市。

做题思路(错解):拿到这道题时,虽然知道这是一道图论题,但并没有想到该题与最优路径有关,以为可以用动态规划来解决,导致解题错误。


解题思路(正解):根据题意,要求最大值最小,自然想到用二分猜答案来解决。对于每猜一个答案,因为答案表示的是小红所走过的城市中交费的最大值,所以如果有城市的收费大于了猜的答案,就应该把它从图中删掉(注意,这里并不是真正把该城市删掉,而是通过条件忽略该城市,即假删除),又因为小红的车的油箱容量为s,中途不加油,所以如果答案可行,则在删掉不满足条件的城市后,从u城市出发能够到达v城市,并且所耗的油量不超过s,这里就要用到图论知识的最优路径了,只要找出了u城市到v城市的最短路径,判断该路径下的耗油量与s的关系,即可确定答案是否可行。如果答案可行,则答案可以再小些,否则,答案就要再大些。

需要注意的是,在求最优路径时,方法有三种——floyd算法、dijstra算法、SPFA算法,但对于该题,floyd算法使用的数组会超内存,并且会超时,SPFA算法的用时不太稳定,在评测时也会有部分数据超时,所以只能使用用时稳定的dijstra算法。本题使用dijstra算法的时间复杂度为O(N*M*k),k为二分猜答案的次数。


#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=10005;
const int inf=1000000010;
int N,M,u,v,s,x,y,z;
int f[maxn];
vectorg[maxn],w[maxn];
struct data
{
	int id,d;
	friend bool operator < (data aa,data bb)
	{
		return aa.d>bb.d;
	}
};
int d[maxn];
bool DIJ(int x)
{
	for(int i=1;i<=N;i++)
	d[i]=inf;
	priority_queueq;
	q.push((data){u,0});
	while(!q.empty())
	{
		data t=q.top(); q.pop();
		int i=t.id;
		if(f[i]>x)  continue;  //将收费大于所猜答案的城市忽略(假删除)
		if(t.d>d[i])  continue;
		d[i]=t.d;
		for(int k=0;kx)  continue;  //注意这里要再次进行假删除
			if(d[i]+c

考后反思:通过这道题可以看出,自己对于知识的熟悉程度还不够,对于一些题目不能及时准确地找到解题方法。

你可能感兴趣的:(分治算法,图论知识,竞赛测验)