Cpp环境【Code[VS]4175】【CQYZOJ1824】收费站
2016-09-10 17:43
429 查看
【问题描述】
在某个遥远的国家里,有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,可能有两条边连接着相同的城市。
【原题传送矩阵】
Code[VS]4175 传送矩阵
CQYZOJ 1827 传送矩阵
【思路梳理】
不算难的图论题,二分猜答案+Dijstra算法就可以通过,类似于笔者写过的另一道题目盛夏的果实,区别在于本题限制更多,难度更大。
考虑到本题的边数目较多,所以SPFA是只能通过70%数据的,Floyd更不要考虑(使用Kruskal优化后也只能通过80%的数据),原因很简单,SPFA是BFS的升级版,每一个点都有可能会多次进入队列(如果不存在负权回路的话,最大情况每个点都可能会进入队列n-1次),SPFA时间复杂度不稳定且反复计算dist浪费了大量的时间;而根据题目的描述显然不可能出现负权路,那么我们可以放心使用Dijstra。
大致思路如下:先猜测最大值最小的点权值,那么在进行Dijstra时,凡是点权值大于猜测值的点都应该被我们用条件掐掉,检视是否能在目前的情况下建立起一条从u到v的最小路,使得dist[v]<=s:如果不能说明被掐掉的点太多,我们应该在右半个区间进行猜测;否则说明该答案可行,检视能否让该点的值更小、掐掉更多的点。
注意细节,提升算法水平!细心点,可以一次通过的!
【Cpp代码】
#include<cstdio> #include<queue> #include<vector> #include<cstring> #include<queue> #include<iostream> #include<algorithm> #define maxn 10005 #define inf 1000000005 using namespace std; int n,m,start,end,s,fare[maxn],ans=inf; bool ok=false; vector<int>g[maxn],w[maxn]; int d[maxn]; struct data { int id,dist; friend bool operator<(data a,data b) { return a.dist>b.dist; } }; bool Dijstra(int mid)//朴素的Dijstra,不赘述细节 { priority_queue<data>q; for(int i=1;i<=n;i++) d[i]=inf; d[start]=0; q.push((data){start,0}); while(!q.empty()) { int i=q.top().id,dist=q.top().dist; q.pop(); if(fare[i]>mid) continue; if(d[i]>dist || dist>s) continue;//注意第二个条件:如果到达i点所需要的油量已经大于了油箱容量、不能达到i点,显然更不可能到达v点(一定有d[v]>d[i]) d[i]=dist; for(int j=0;j<g[i].size();j++) { int k=g[i][j],c=w[i][j]; if(fare[k]>mid) continue;//假删除一些点 if(d[k]>dist+c)//松弛操作 { d[k]=dist+c; q.push((data){k,d[k]}); } } if(d[end]<=s)//不一定需要等到所有的边都考虑完再判断是否能到达终点,降低实际耗时 { ok=true;//ok:如果至少有一次到达了终点,那么ok=1 return true; } } return false; } void solve()//二分猜答案,猜测最大值最小的点权 { int left=0,right=inf; while(left<=right) { int mid=(left+right)/2; if(Dijstra(mid)) right=mid-1; else left=mid+1; } if(ok) cout<<left;//至少有一种方案能到达终点 else cout<<"-1";//一次都不可能到达终点 } int main() { scanf("%d%d%d%d%d",&n,&m,&start,&end,&s); for(int i=1;i<=n;i++) scanf("%d",&fare[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); } if(start!=end) solve();//如果起点终点一样直接输出该点点权就可 else cout<<fare[start]; return 0; }
相关文章推荐
- Cpp环境【CQYZOJ1836】【Code[VS]5230】猴子
- vscode cpp cmake 环境搭建
- Cpp环境【SDUT1128】【Code[VS]1809】【CQYZOJ1823】河床
- Cpp环境【Code[VS]1084】【NOIP2003普及组】乒乓球
- Cpp环境【Code[VS]5227】【JSOI2010】盛夏的果实
- Cpp环境【NOIP2003 P3】【Vijos1100】【Code[VS]1090】【CQYZOS2816】加分二叉树
- codevs 4175 收费站(二分+spfa)
- Cpp环境【NOIP2006提高组】【Code[VS]1155】【Vijos1399】 金(精)明的预算
- Cpp环境【NOIP2015 D1P2】【Viijos1979】【Code[VS] 4511】【CQYZOS3198】 信息传递
- Cpp环境【TYVJ1153】【Code[VS]4093】【CQYZOJ16874】 间谍网络
- codevs 4175 收费站
- Cpp环境【Code[VS]5226】物品选取
- [codevs4175]收费站
- codevs 4175 收费站(二分+SPFA)
- Cpp环境【NOIP2010提高组】【Vijos1777】【Code[VS]1066】【CQYZOJ1793】引水入城
- 洛谷P1951 BSOJ2636 CODEVS4175 收费站
- Cpp环境【CQYZOJ1496】【Code[VS]5287】搬家大冒险
- Cpp环境【Tyvj1011】【Code[VS]1169】传纸条
- .NET Core VS Code 环境配置
- VSCode python环境运行搭建