CF581E,JZOJ4689新车

题目描述

Keith家(多个)在城区,从城区到学校的路可以抽象成一个数轴,Keith家的坐标为W,学校的坐标为E。路上有很多加油站,每个加油站能提供92#,95#,98#三种汽油中的一种。由于Keith不缺钱,每到一个加油站,他都能加任意多的油。由于道路是双向的,Keith的车既能往左开,也能往右开。
1升汽油可以跑1千米,这与油的种类无关。车的油箱容量是S升,任何时候油箱中的油都不能超过S升,但是作为Keith大神的御用座驾,这台车有点与众不同,它的油箱中能同时存在多种汽油。出发时,油箱是装满98#汽油的。
众所周知的是,98#汽油最好,92#最差。由于Keith很爱惜自己的新车,所以他希望用尽可能少的92#汽油,如果有多种可能用的92#同样多,那就要求用的95#尽可能少。
现在Keith告诉你N个家的坐标Wi,学校的坐标E,以及路上所有加油站的坐标,求从各个家分别到学校的最优策略中,需要消耗的最少92#和95#汽油。

1 ≤ N , M ≤ 200000 , 0 ≤ Wi , Xi , E , S ≤ 10^9且Wi ≤ E , 1 ≤ Ti ≤ 3

分析

比赛的时候想到的贪心策略比较麻烦,没有简化,以致没法dp过去,而且那时候时间紧,暴力完后就赶紧想第一题了(也没写粗来···)。
首先把家和学校看成98加油站。然后贪心策略:从当前加油站出发,如果在s范围内有油品相同或者更好的,就走到最近的那个;如果没有,就尽量走远一点,并优先在油品比较好的加油站里停下加油,这里不一定加满。(当然不能停在另一个家)
然后设个f[i][0\1]表示在第i个加油站出发,此时车没油,要到达学校的所要用的92\95。从学校往左边跑就行了。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=2000005;
struct rec
{
    int x,t,rev;
}b[N];
int e,s1,n,m,w[N],i,j,dur,s[4],d[4][N],f[N][2],ans[N][2],tt,tar,t[4];
bool cmp(rec a,rec b)
{
    return (a.x<b.x)||(a.x==b.x&&(a.t<b.t));
}
int main()
{
    freopen("car.in","r",stdin);
    freopen("car.out","w",stdout);
    scanf("%d %d %d %d",&e,&s1,&n,&m);
    fo(i,1,n) scanf("%d%d",&b[i].t,&b[i].x);
    b[n+1].x=e;
    b[n+1].t=3;
    tt=n+1;
    fo(i,1,m) 
    {
        scanf("%d",w+i);
        b[++tt].x=w[i];
        b[tt].t=4;
        b[tt].rev=i;
    }
    sort(b+1,b+1+tt,cmp);
    fo(i,1,tt)
        if (b[i].x==e&&b[i].t==3)
        {
            tt=i;
            break;
        }
    f[tt][0]=f[tt][1]=0;
    s[1]=s[2]=s[3]=1;
    t[1]=t[2]=t[3]=0;
    t[3]=1;
    d[3][s[3]]=tt;
    fd(i,tt-1,1)
    {
        if (b[i+1].x==b[i].x&&b[i].t<b[i+1].t&&b[i+1].t!=4) continue;
        fo(j,1,3)
            while (s[j]<=t[j]&&b[d[j][s[j]]].x-b[i].x>s1) s[j]++;
        tar=0;
        fo(j,min(b[i].t,3),3)
            if (s[j]<=t[j]&&(tar==0||b[d[j][t[j]]].x<b[d[tar][t[tar]]].x))
                tar=j;
        if (tar)
        {
            f[i][0]=f[d[tar][t[tar]]][0];
            f[i][1]=f[d[tar][t[tar]]][1];
            if (b[i].t<3) 
                f[i][b[i].t-1]+=b[d[tar][t[tar]]].x-b[i].x;
        }
        else
        {
            fd(j,3,1)
                if (s[j]<=t[j])
                    break;
            if (!j)
            {
                fo(j,1,i) f[j][0]=f[j][1]=-1;
                break;
            }
            f[i][0]=f[d[j][s[j]]][0];
            f[i][1]=f[d[j][s[j]]][1];
            if (b[i].t<3)
                f[i][b[i].t-1]+=s1;
            f[i][b[d[j][s[j]]].t-1]-=s1-(b[d[j][s[j]]].x-b[i].x);
        }
        if(b[i].t!=4)   
            d[b[i].t][++t[b[i].t]]=i;
    }
    fo(i,1,tt)  
        ans[b[i].rev][0]=f[i][0],ans[b[i].rev][1]=f[i][1];
    fo(i,1,m)
        printf("%d %d\n",ans[i][0],ans[i][1]);
}

反思

1,改题的时候太累了,改了一个小时,精神很重要。
2,贪心一定能简化就简化,这样写起来也容易点。
3,对于这个贪心不复杂的题目,大数据错就要多读程序,要在调试方法的耗时找个平衡点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值