洛谷P1016 旅行家的预算 [单调队列]

传送门
题意:给出n个加油站的位置posi和重点t以及每个加油站油的价格vi以及油箱的体积L,求最小花费

题解:如果油箱容量是无限大的那么显然可以直接使用优先队列每经过一个加油站就直接丢进去价格然后每次需要加油就弹出即可,而这里限制了油箱体积为L,就不能无脑无限量地使用最小值,此时维护一个价格单调递增的队列,每次选取队列头的油进行消耗,每次经过加油站,弹出尾部比当前油费贵的油,然后插入体积为L-油箱内剩余的油的体积的当前油站的油即可

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;

struct nod{
    double pos;
    double val;
}no[8];

struct que{
    double val;
    double res;
}q[8];

bool cmp(struct nod a,struct nod b){
    return a.pos<b.pos;
}

int main(){
    double d1,c,d2,p;
    int n;
    scanf("%lf%lf%lf%lf%d",&d1,&c,&d2,&p,&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&no[i].pos,&no[i].val);
    sort(no+1,no+1+n,cmp);
    int head=1;
    int tail=0;
    q[++tail].res=c;
    q[tail].val=p;
    double ans=p*c;
    double sum=c;
    int F=1;
    for(int i=1;i<=n;i++){
        if(d1<=no[i].pos){
            double x=(d1-no[i-1].pos)/d2;
            while(head<=tail&&x){
                if(x<q[head].res){
                    q[head].res-=x;
                    x=0;
                    break;
                }
                else{
                    x-=q[head].res;
                    head++;
                }
            }
            if(x)F=0;
            while(head<=tail){
                ans-=q[head].res*q[head].val;
                head++;
            }
        }
        else{
            double x=(no[i].pos-no[i-1].pos)/d2;
            sum-=x;
            while(head<=tail&&x){
                if(x<q[head].res){
                    q[head].res-=x;
                    x=0;
                    break;
                }
                else{
                    x-=q[head].res;
                    head++;
                }
            }
            if(x){F=0;break;}
            while(head<=tail&&q[tail].val>=no[i].val){
                sum-=q[tail].res;
                ans-=q[tail].res*q[tail].val;
                tail--;
            }
            q[++tail].res=c-sum;
            ans+=(c-sum)*no[i].val;
            q[tail].val=no[i].val;
            sum=c;
        }
    }
    if(F&&d1>no[n].pos){
            double x=(d1-no[n].pos)/d2;
            while(head<=tail&&x){
                if(x<q[head].res){
                    q[head].res-=x;
                    x=0;
                    break;
                }
                else{
                    x-=q[head].res;
                    head++;
                }
            }
            if(x)F=0;
            while(head<=tail){
                ans-=q[head].res*q[head].val;
                head++;
            }
    }
    if(F)printf("%.2f\n",ans);
    else printf("No Solution\n");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值