Gym - 101572A Airport Coffee 单调队列优化DP

Description

在 一 个 一 维 坐 标 轴 上 , 一 个 人 要 从 0 位 置 走 到 位 置 l 。 在一个一维坐标轴上,一个人要从0位置走到位置l。 0l
他 的 默 认 速 度 为 a , 他 希 望 在 最 短 时 间 到 达 位 置 l 他的默认速度为a,他希望在最短时间到达位置l al
幸 运 的 是 在 坐 标 轴 上 存 在 n 个 咖 啡 厅 , 他 可 以 在 任 意 咖 啡 厅 买 一 杯 咖 啡 幸运的是在坐标轴上存在n个咖啡厅,他可以在任意咖啡厅买一杯咖啡 n
他 需 要 t 秒 的 时 间 使 得 咖 啡 冷 却 ( 冷 却 的 过 程 他 保 持 速 度 a 移 动 ) , 之 后 r 秒 他 在 喝 咖 啡 , 喝 咖 啡 时 的 速 度 为 b 。 ( b > a ) 他需要t秒的时间使得咖啡冷却(冷却的过程他保持速度a移动),之后r秒他在喝咖啡,喝咖啡时的速度为b。(b > a) t使(a)rb(b>a)
他 有 且 仅 能 有 一 杯 咖 啡 在 手 上 , 但 是 他 可 以 把 没 喝 完 的 咖 啡 扔 了 , 在 新 的 咖 啡 店 重 新 购 买 。 他有且仅能有一杯咖啡在手上,但是他可以把没喝完的咖啡扔了,在新的咖啡店重新购买。
问 怎 么 购 买 咖 啡 才 能 最 短 到 达 位 置 l , 输 出 购 买 顺 序 。 问怎么购买咖啡才能最短到达位置l,输出购买顺序。 l


Input

1 ≤ l ≤ 1 0 11 1\leq l \leq 10^{11} 1l1011
1 ≤ a &lt; b ≤ 200 1\leq a &lt; b \leq 200 1a<b200
0 ≤ t ≤ 300 0\leq t \leq 300 0t300
1 ≤ r ≤ 1200 1\leq r \leq 1200 1r1200


Output

1 购 买 次 数 1 购买次数 1
2 购 买 咖 啡 厅 编 号 2 购买咖啡厅编号 2


Solution

设 f i 表 示 考 虑 前 i 个 咖 啡 厅 走 到 第 i 个 咖 啡 厅 的 最 短 时 间 设f_i表示考虑前i个咖啡厅走到第i个咖啡厅的最短时间 fiii
f i 初 始 为 a r r [ i ] a , 即 表 示 不 购 买 咖 啡 直 接 走 到 第 i 个 咖 啡 厅 位 置 。 f_i 初始为 \frac {arr[i]}{a},即表示不购买咖啡直接走到第i个咖啡厅位置。 fiaarr[i]i
然 后 有 f i = m i n ( f j + c o s t ( j , i ) ) ( 0 &lt; j &lt; i ) 然后有f_i = min(f_j+cost(j,i)) (0&lt;j&lt;i) fi=min(fj+cost(j,i))(0<j<i)
考 虑 在 第 j 个 咖 啡 厅 购 买 咖 啡 后 从 j → i 的 时 间 c o s t ( j , i ) 有 多 种 取 值 考虑在第j个咖啡厅购买咖啡后从j \to i的时间cost(j,i)有多种取值 jjicost(j,i)
设 l i m 1 = t ∗ a + r ∗ b : 设lim1 = t*a+r*b: lim1=ta+rb: 表示从冷却到喝完咖啡的最大距离
设 l i m 0 = t ∗ a : 设lim0 = t*a: lim0=ta: 表示咖啡刚好冷却完的距离。
c o s t ( j , i ) 分 为 一 下 三 种 情 况 cost(j,i) 分为一下三种情况 cost(j,i)
1. a r r [ i ] − a r r [ j ] ≥ l i m 1 1.arr[i] - arr[j] \ge lim1 1.arr[i]arr[j]lim1
         c o s t ( j , i ) = a r r [ i ] − a r r [ j ] − l i m 1 a + t + r \ \ \ \ \ \ \ \ cost(j,i) = \frac{arr[i]-arr[j]-lim1}{a}+t+r         cost(j,i)=aarr[i]arr[j]lim1+t+r
2. l i m 0 ≤ a r r [ i ] − a r r [ j ] &lt; l i m 1 2.lim0 \leq arr[i]-arr[j]&lt;lim1 2.lim0arr[i]arr[j]<lim1
         c o s t ( j , i ) = a r r [ i ] − a r r [ j ] − l i m 0 b + t \ \ \ \ \ \ \ \ cost(j,i) = \frac{arr[i]-arr[j]-lim0}{b}+t         cost(j,i)=barr[i]arr[j]lim0+t
3. a r r [ i ] − a r r [ j ] &lt; l i m 0 3.arr[i]-arr[j]&lt;lim0 3.arr[i]arr[j]<lim0
         c o s t ( j , i ) = a r r [ i ] − a r r [ j ] a \ \ \ \ \ \ \ \ cost(j,i) = \frac{arr[i]-arr[j]}{a}         cost(j,i)=aarr[i]arr[j]

1. a r r [ i ] − a r r [ j ] ≥ l i m 1 1.arr[i] - arr[j] \ge lim1 1.arr[i]arr[j]lim1
         f i = f j + c o s t ( j , i ) = f j − a r r [ j ] a + a r r [ i ] − l i m 1 a + t + r \ \ \ \ \ \ \ \ f_i=f_j+cost(j,i) = \color {red} {f_j-\frac{arr[j]}{a}}+\frac{arr[i]-lim1}{a}+t+r         fi=fj+cost(j,i)=fjaarr[j]+aarr[i]lim1+t+r
         对 于 a r r [ j ] ≤ a r r [ i ] − l i m 1 , 仅 需 要 对 满 足 条 件 的 j 维 护 f j − a r r [ j ] a 的 最 小 值 即 可 , 不 需 要 优 先 队 列 。 \ \ \ \ \ \ \ \ 对于arr[j] \le arr[i]-lim1,仅需要对满足条件的j维护\color {red} {f_j-\frac{arr[j]}{a}}的最小值即可,不需要优先队列。         arr[j]arr[i]lim1jfjaarr[j]
2. l i m 0 ≤ a r r [ i ] − a r r [ j ] &lt; l i m 1 2.lim0 \leq arr[i]-arr[j]&lt;lim1 2.lim0arr[i]arr[j]<lim1
         f i = f j + c o s t ( j , i ) = f j − a r r [ j ] b + a r r [ i ] − l i m 0 a + t \ \ \ \ \ \ \ \ f_i=f_j+cost(j,i) = \color {red} {f_j-\frac{arr[j]}{b}}+\frac{arr[i]-lim0}{a}+t         fi=fj+cost(j,i)=fjbarr[j]+aarr[i]lim0+t
         对 于 a r r [ j ] ≤ a r r [ i ] − l i m 0 时 将 f j − a r r [ j ] b 加 入 优 先 队 列 。 \ \ \ \ \ \ \ \ 对于arr[j] \le arr[i]-lim0时将 \color {red} {f_j-\frac{arr[j]}{b}}加入优先队列。         arr[j]arr[i]lim0fjbarr[j]
         对 于 a r r [ j ] ≤ a r r [ i ] − l i m 1 时 将 优 先 队 列 中 队 头 元 素 出 队 以 满 足 条 件 a r r [ j ] &gt; a r r [ i ] − l i m 1 。 \ \ \ \ \ \ \ \ 对于arr[j] \le arr[i]-lim1时将 优先队列中队头元素出队以满足条件arr[j] &gt; arr[i]-lim1。         arr[j]arr[i]lim1arr[j]>arr[i]lim1
3. 由 于 最 优 性 , 第 三 种 是 前 两 种 的 子 集 , 即 最 优 值 不 会 出 现 在 第 三 种 情 况 下 。 3.由于最优性,第三种是前两种的子集,即最优值不会出现在第三种情况下。 3.,


Codes

O n e One One
三 个 优 先 队 列 模 拟 , 其 实 只 用 一 个 就 够 了 , 最 后 的 输 出 格 式 让 我 对 拍 了 一 上 午 。 明 明 能 一 遍 A C 的 。 三个优先队列模拟,其实只用一个就够了,最后的输出格式让我对拍了一上午。明明能一遍AC的。 AC

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,double> node;
const double inf = 1ll<<62; 
const int maxn = 5e5+10;
ll arr[maxn],l,a,b,t,r;
int n,ans[maxn],last[maxn],cnt;
double dp[maxn];
node qu1[maxn],qu2[maxn],qu3[maxn];
int main()
{
    while(~scanf("%lld%lld%lld%lld%lld",&l,&a,&b,&t,&r)) {
        scanf("%d",&n);
        cnt = 0;
        memset(dp,0,sizeof(dp));
        memset(last,0,sizeof(last));
        for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);arr[0] = 0;
        arr[++n] = l;
        ll x = a*t,y = a*t+b*r;
        int h1 = 1,t1 = 0,h2 = 1,t2 = 0,h3 = 1,t3 = 0;
        int pos1 = 1,pos2 = 1;
        for(int i=1;i<=n;i++) {
            while(i > pos1 && arr[pos1] + x < arr[i]) {
                double it2 = dp[pos1]-1.0*arr[pos1]/b;
                while(h2 <= t2 && qu2[t2].second >= it2) t2--;
                qu2[++t2] = make_pair(pos1,it2);
                pos1++;
            }
            while(i > pos2 && arr[pos2] + y < arr[i]) {
                double it2 = dp[pos2]-1.0*arr[pos2]/a;
                while(h3 <= t3 && qu3[t3].second >= it2) t3--;
                qu3[++t3] = make_pair(pos2,it2);
                pos2++;
            }
            //printf("1\n");
            if(i == 1) dp[1] = 1.0*arr[i] / a;
            else {
                dp[i] = inf;
                while(h1 <= t1 && arr[qu1[h1].first]+x < arr[i]) h1++;
                if(h1 <= t1) {
                    if(dp[i] > 1.0*arr[i]/a+qu1[h1].second) {
                        dp[i] = 1.0*arr[i]/a+qu1[h1].second;
                        last[i] = qu1[h1].first;
                    }
                }
                while(h2 <= t2 && arr[qu2[h2].first]+y < arr[i]) h2++;
                if(h2 <= t2) {
                    if(dp[i] > 1.0*(arr[i]-x)/b+t+qu2[h2].second) {
                        dp[i] = 1.0*(arr[i]-x)/b+t+qu2[h2].second;
                        last[i] = qu2[h2].first;
                    }
                }
                if(h3 <= t3) {
                    if(dp[i] > 1.0*(arr[i]-y)/a+t+r+qu3[h3].second) {
                        dp[i] = 1.0*(arr[i]-y)/a+t+r+qu3[h3].second;
                        last[i] = qu3[h3].first;
                    }
                }
            }
            double it1 = dp[i]-1.0*arr[i]/a;
            while(h1 <= t1 && qu1[t1].second >= it1) t1--;
            qu1[++t1] = make_pair(i,it1);
        }
        int cur = n;
        while(last[cur]) {
            ans[cnt++] = last[cur];
            cur = last[cur];
        }
        printf("%d\n",cnt);
        for(int i=cnt-1;i>=0;i--) printf("%d ",ans[i]-1);
        // 写自闭了 MDZZ
        // for(int i=cnt-1;i>0;i--) printf("%d ",ans[i]-1);printf("%d\n",ans[0]-1);
    }
    return 0;
}

T w o Two Two
更简单明了的写法

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;
const double inf = 1e20;
typedef long long ll;
typedef pair<int,double> Node;
Node qu[maxn];
int n,path[maxn],cnt,ans[maxn];
ll L,a,b,t,r,arr[maxn];
double dp[maxn];
int main()
{
    while(~scanf("%lld%lld%lld%lld%lld",&L,&a,&b,&t,&r)){
        memset(path,0,sizeof(path));cnt = 0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);arr[++n]=L;
        ll lim0 = a*t,lim1 = a*t+b*r;
        double dp0 = inf;
        int id0=0,head=1,tail=0;
        for(int i=1,j=1,k=1;i<=n;i++) {
            dp[i] = 1.0*arr[i]/a;
            /// arr[i] - arr[j] >= lim1 
            while(j < i && arr[i] - arr[j] >= lim1) {
                double tmp = dp[j] - 1.0*arr[j] / a;
                if(tmp < dp0) {
                    dp0 = tmp;
                    id0 = j;
                }
                j++;
            }
            if(id0) {
                double tmp = dp0 + t + r + 1.0*(arr[i]-lim1) / a;
                if(tmp < dp[i]) {
                    dp[i] = tmp;
                    path[i] = id0;
                }
            }
            /// lim0<= arr[i] - arr[j] <lim1
            while(k < i && arr[i] - arr[k] >= lim0) {
                double tmp = dp[k] - 1.0*arr[k]/b;
                while(head <= tail && qu[tail].second > tmp) tail--;//
                qu[++tail] = make_pair(k,tmp);
                k++;
            }
            while(head<=tail && arr[i]-arr[qu[head].first] >= lim1) head++;
            if(head <= tail) {
                double tmp = qu[head].second+t+1.0*(arr[i]-lim0)/b;
                if(tmp < dp[i]) {
                    dp[i] = tmp;
                    path[i] = qu[head].first;
                }
            }
        }
        int id = n;
        while(path[id]) {
            ans[++cnt] = path[id];
            id = path[id];
        }
        printf("%d\n",cnt);
        for(int i=cnt;i;i--) {
            if(i == 1) printf("%d\n",ans[i]-1);
            else printf("%d ",ans[i]-1);
        }
        // 写自闭了 MDZZ
        // for(int i=cnt;i>1;i--) printf("%d ",ans[i]-1);
        // printf("%d\n",ans[1]);
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值