您的位置:首页 > 其它

CODEVS 4175 收费站 二分+SPFA

2016-09-26 20:49 225 查看
题目描述 Description

在某个遥远的国家里,有n个城市。编号为1,2,3,……,n。

这个国家的政府修建了m条双向的公路。每条公路连接着两个城市。沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。

开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。所有的收费站都在城市中,在城市间的公路上没有任何的收费站。

小红现在要开车从城市u到城市v(1<=u,v<=n)。她的车最多可以装下s升的汽油。在出发的时候,车的油箱是满的,并且她在路上不想加油。

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

输入描述 Input Description

第一行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升汽油。

输出描述 Output Description

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

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

数据范围及提示 Data Size & Hint

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

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

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

洛谷 P1462 通往奥格瑞玛的道路(和这个题简直一毛一样)

来填一个暑假里的旧坑…..

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

从u开始跑最短路,如果dist[v]>S的话,就puts(“-1”);

重点是二分部分:

我在我的代码中用dq[i]表示每个城市的过路费,

l是min(dq[u],dq[v]),r是max dq[i],

对于每一个mid(在spfa里面是cost),跑一次spfa,如果当前点连接着的点的过路费dq[e.to]>cost,就直接略过这个点,跑完以后看dist[v],如果dist[v]>S,说明当前的mid(cost)太小,需要增大,如果dist[v]<=S,说明当前的mid太大,需要减小(如果dist[v]==S时,如果mid增大,解不会更优),当dist[v]<=S时,ans和mid取个min。

PS:SPFA一定要用双端优化…..(好像有个学长说过NOIP不会卡双端)

(后面有彩蛋)

/*
本题中使用的时间优化:
1、读入优化
2、双端队列spfa优化
*/
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=200000+500;
const ll inf=20000000000000000ll;
struct Edge
{
int f;
int to;
ll d;
int next;
}edge[maxn];
int n,m,u,v;
int dq[maxn];
int head[maxn];
bool vis[maxn];
ll dist[maxn];
ll S;
int mincost;
int maxcost;
int tot;
void add(int f,int t,int d)
{
edge[++tot].f=f;
edge[tot].to=t;
edge[tot].d=d;
edge[tot].next=head[f];
head[f]=tot;
}
deque<int>q;
void clr()
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
dist[i]=inf;
}
bool spfa1()
{
clr();
q.push_front(u);
dist[u]=0;
while(!q.empty())
{
int x=q.front();
vis[x]=0;
q.pop_front();
for(int i=head[x];i;i=edge[i].next)
{
Edge e=edge[i];
if(dist[e.to]>dist[x]+e.d)
{
dist[e.to]=dist[x]+e.d;
if(!vis[e.to])
{
vis[e.to]=1;
if(!q.empty())
{
if(dist[e.to]<dist[q.front()])
q.push_front(e.to);
else
q.push_back(e.to);
}
else
q.push_back(e.to);
}
}
}
}
if(dist[v]>S)
return false;
return true;
}
ll maxway=inf;
bool spfa(int cost)
{
if(dq[u] > cost || dq[v] > cost) return 0;
clr();
q.push_front(u);
dist[u]=0;
while(!q.empty())
{
int x=q.front();
vis[x]=0;
q.pop_front();
for(int i=head[x];i;i=edge[i].next)
{
Edge e=edge[i];
if(dq[e.to]>cost)
continue;
else
{
if(dist[e.to]>dist[x]+e.d)
{
dist[e.to]=dist[x]+e.d;
if(!vis[e.to])
{
if(!q.empty())
{
if(dist[e.to]<dist[q.front()])
q.push_front(e.to);
else
q.push_back(e.to);
}
else
q.push_back(e.to);
vis[e.to]=1;
}
}
}
}
}
ll hah=dist[v];
if(hah>S)
{
return true;
}//到不了了,缴费需增大
else if(hah<=S)//能到
{
return false;
}
}
// 缴费↑,能走的城市↑,dist v↓,
// 缴费↓,能走的城市↓,dist v↑,
int div()
{
int ans=maxcost;
int l=mincost;
int r=maxcost;
while(l<=r)
{
int mid=(l+r)>>1;
if(spfa(mid))
{
l=mid+1;
}
else
{
ans=min(ans,mid);//不能到你更新个鬼咯
r=mid-1;
}
//  printf("l:%d r:%d mid:%d\n",l,r,mid);
}
return ans;
}
int read(int &a)
{
char c=getchar();
while(c>'9'||c<'0')
{
c=getchar();
}
int ans=0;
while(c>='0'&&c<='9')
{
ans=(ans<<3)+(ans<<1);
ans+=c-'0';
c=getchar();
}
a=ans;
return a;
}
ll readl(ll &a)
{
char c=getchar();
while(c>'9'||c<'0')
{
c=getchar();
}
ll ans=0;
while(c>='0'&&c<='9')
{
ans=(ans<<3)+(ans<<1);
ans+=(ll)(c-'0');
c=getchar();
}
a=ans;
return a;
}
int main()
{
freopen("cost.in","r",stdin);
freopen("cost.out","w",stdout);
read(n),read(m),read(u),read(v),readl(S);
for(int i=1;i<=n;i++)
read(dq[i]),maxcost=max(maxcost,dq[i]);
for(int i=1;i<=m;i++)
{
int a,b;
ll c;
read(a),read(b),readl(c);
add(a,b,c);
add(b,a,c);
}
mincost=max(dq[u],dq[v]);
if(!spfa1())
puts("-1");
else
printf("%d",div());
return 0;
}


BUT!


在洛谷交题,就死活T掉一个点。

还能再快点么?

答案是肯定的,我们在输入每个城市收的费用dq[i]时开一个数组hah[i]==dq[i],

然后hah小到大sort一遍,直接从hah里面二分,(这样最起码少了好几个常数,log(2) 10000≈13,log(2)1000000000≈30,由于spfa时间复杂度玄学,这样时间少了二分之一)



23333时间真的快了不少(在codevs上第一次交最后一个点卡800ms过的,优化后最后一个点只用了300多ms)

二分部分代码如下:

int div()
{
int ans=n;
int l=1;
int r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(spfa(hah[mid]))
{
l=mid+1;
}
else
{
ans=min(ans,mid);
r=mid-1;
}
//  printf("l:%d r:%d mid:%d\n",l,r,mid);
}
return hah[ans];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: