CODEVS 4175 收费站 二分+SPFA

题目描述 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];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值