【贪心+ST算法+单调栈】51Nod1288[汽油补给]题解

本文介绍了一道经典的贪心算法题目,通过最优选择策略来解决旅行中的油费最小化问题。利用数据结构如RMQ算法和单调栈优化了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目概述

n+1 个城市, 0 是起点 n 是终点,开车从 012n ,车每走 1 个单位距离消耗 1 个单位的汽油,油箱的容量是 T 。给出每个城市到下一个城市的距离 D ,以及当地的油价 P ,求走完整个旅途最少的花费。如果无法从起点到达终点输出 1

解题报告

以前做过的贪心题……贪心想法不难,主要是要考虑完整。

0 开始,在能走到的点中选出油价比 0 小的点,如果:

  • 有,那么加能恰好走到该点的油,走到该点。
  • 没有,那么在 0 加满油,走到油价最小的点。

如此重复即可。

以前做的时候 O(n2) ……现在发现用点数据结构(比如ST算法和单调栈)就可以快很多……

示例程序

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100000,Log=16,MAXINT=((1<<30)-1)*2+1;

int n,now,MAX,cst[maxn+5];LL dis[maxn+5],ans;
int top,stk[maxn+5],nxt[maxn+5],RMQ[maxn+5][Log+5];

#define Eoln(x) ((x)==10||(x)==13||(x)==EOF)
inline char readc()
{
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;return *l++;
}
inline int readi(int &x)
{
    int tot=0,f=1;char ch=readc(),lst='+';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=(tot<<1)+(tot<<3)+ch-48,ch=readc();
    return x=tot*f,Eoln(ch);
}
#define Miner(x,y) (cst[x]<cst[y]?x:y)
void Make()
{
    for (int j=1,k=log2(n);j<=k;j++)
    for (int i=1;i<=n-(1<<j)+1;i++)
        RMQ[i][j]=Miner(RMQ[i][j-1],RMQ[i+(1<<j-1)][j-1]);
    for (int i=n;i>=1;i--)
    {
        while (top&&cst[i]<=cst[stk[top]]) top--;
        nxt[i]=stk[top];stk[++top]=i;
    }
}
inline int Min(int L,int R) {int j=log2(R-L+1);return Miner(RMQ[L][j],RMQ[R-(1<<j)+1][j]);}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(n);readi(MAX);
    for (int i=1,x;i<=n;i++)
    {
        readi(x);readi(cst[RMQ[i][0]=i]);
        dis[i+1]=dis[i]+x;
    }
    n++;cst[RMQ[n][0]=n]=MAXINT;Make();
    for (int i=1,R=1,j;i<n;now-=dis[j]-dis[i],i=j)
    {
        while (R<=n&&dis[R]<=dis[i]+MAX) R++;
        if (i==R-1) return printf("-1\n"),0;
        if (nxt[i]&&nxt[i]<R)
        {
            j=nxt[i];LL tem=max(dis[j]-dis[i]-now,(LL)0);
            ans+=tem*cst[i];now+=tem;
        } else
        {
            if (dis[i]+MAX>=dis[n]) {ans+=max(dis[n]-dis[i]-now,(LL)0)*cst[i];break;}
            j=Min(i+1,R-1);ans+=(LL)(MAX-now)*cst[i];now=MAX;
        }
    }
    return printf("%lld\n",ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值