洛谷 P1951 收费站_NOI导刊2009提高(2) 最短路+二分
#[TOC]
#题面
##题目链接
其实还有一道双倍经验
##题目描述
在某个遥远的国家里,有n个城市。编号为1,2,3,…,n。
这个国家的政府修建了m条双向的公路。每条公路连接着两个城市。沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。
开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。所有的收费站都在城市中,在城市间的公路上没有任何的收费站。
小红现在要开车从城市u到城市v(1 \(\leq u,v \leq n\) )。她的车最多可以装下 \(s\) 升的汽油。在出发的时候,车的油箱是满的,并且她在路上不想加油。
在路上,每经过一个城市,她都要交一定的费用。如果某次交的费用比较多,她的心情就会变得很糟。所以她想知道,在她能到达目的地的前提下,她交的费用中最多的一次最少是多少。这个问题对于她来说太难了,于是她找到了聪明的你,你能帮帮她吗?
##输入输出格式
###输入格式
第一行5个正整数, \(n,m,u,v,s\) ,分别表示有 \(n\) 个城市, \(m\) 条公路,从城市 \(u\) 到城市 \(v\) ,车的油箱的容量为 \(s\) 升。
接下来的有 \(n\) 行,每行1个整数, \(f_i\) 表示经过城市 \(i\) ,需要交费 \(f_i\) 元。
再接下来有 \(m\) 行,每行3个正整数,\(a_i,b_i,c_i (1 \leq a_i,b_i \leq n)\) ,表示城市 \(a_i\) 和城市 \(b_i\) 之间有一条公路,如果从城市 \(a_i\) 到城市 \(b_i\) ,或者从城市 \(b_i\) 到城市 \(a_i\) ,需要 \(c_i\) 升的汽油。
###输出格式
仅一个整数,表示小红交费最多的一次的最小值。
如果她无法到达城市 \(v\) ,输出-1
##输入输出样例
###输入样例:
4 4 2 3 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
###输出样例:
8
##说明
【数据规模】
对于60%的数据,满足 \(n \leq 200,m \leq 10000,s \leq 200\)
对于100%的数据,满足 \(n \leq 10000,m \leq 50000,s \leq 1000000000\)
对于100%的数据,满足 \(c_i \leq 1000000000,f_i \leq1000000000,\) 可能有两条边连接着相同的城市。
【时空限制】
1000ms,128M
#思路
首先看题目的问法,求最大值的最小,可以考虑到二分答案
首先假定当答案是x,那么他肯定要满足如果不经过交费超过x的点,跑一遍从u到v的最短路一定小于总油量
于是可以根据此多次跑最短路了
#AC代码
#include<bits/stdc++.h>
const int maxn=10010;
const int maxm=50010;
using namespace std;
int n,m,st,ed,W,wt[maxn];
int tot,to[maxm<<1],nxt[maxm<<1],head[maxn],len[maxm<<1];
int l,r,dis[maxn];
priority_queue< pair<int,int> > q;
bool vis[maxn],b;
void Dijkstra(int mx)
{
for(int i=1;i<=n;i++) dis[i]=INT_MAX/2;
for(int i=1;i<=n;i++) vis[i]=false;
dis[st]=0;
q.push(make_pair(0,st));
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(wt[v]>mx) continue;
if(dis[u]+len[i]<dis[v])
{
dis[v]=dis[u]+len[i];
q.push(make_pair(-dis[v],v));
}
}
}
}
int main()
{
scanf("%d%d%d%d%d",&n,&m,&st,&ed,&W);
for(int i=1;i<=n;i++)
{
scanf("%d",&wt[i]);
r=max(r,wt[i]);
}
for(int i=1;i<=m;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
to[++tot]=v;nxt[tot]=head[u];len[tot]=w;head[u]=tot;
to[++tot]=u;nxt[tot]=head[v];len[tot]=w;head[v]=tot;
}
l=max(wt[st],wt[ed]);
do ///不要while! 否则如果wt[st]和wt[ed]相等就不能进循环了
{
int mid=(l+r)>>1;
Dijkstra(mid);
if(dis[ed]>W) l=mid+1;
else r=mid,b=true;
}while(l<r);
if(b) printf("%d",r);
else printf("-1");
return 0;
}
#总结
这题第一遍做只得了80分,因为写的是while而不是do while,这个错很隐蔽。。如果l=0肯定就不会出这种情况了(吧)(所以我想说的是这样定左边界很灵性)
另外,判断是否可到终点的时候,放在r这一边也是一时想到(感谢Uranus巨巨)