首页 > 其他 > 详细

洛谷网校:动态规划(二)单调队列优化 p1725,p3572

时间:2020-02-04 21:12:21      阅读:68      评论:0      收藏:0      [点我收藏+]

2020.2.3

动态规划(二)单调队列优化

p1725 琪露诺

题目描述

小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只移动到区间[i+l,i+r]中的任意一格。

每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数。

开始时,琪露诺在编号0的格子上,只要她下一步的位置编号大于N就算到达对岸。

输入格式

第1行:3个正整数N, L, R

第2行:N+1个整数,第i个数表示编号为i-1的格子的冰冻指数A[i-1]

输出格式

一个整数,表示最大冰冻指数。保证不超过2^31^-1

题解(批注加粗)

f~i~表示从起点走到第i个格子的最大收益。

f~i~=max(f~j~+a~i~) (i?r≤j≤i?l)。由j的状态转移到i的状态(递推)

在当前决定的节点之前(用窗口更新当前而不是用当前更新窗口)维护一个滑动窗口,每次求窗口中f~i~的最大值。

用一个单调队列维护窗口里所有可能用到的决策点。

对于两个点x,y,如果x<y且f~x~≤ f~y~,那么y进入窗口后,决策点一定不是x。由于是从前往后递推,若x<y(y比x靠后),那么当前y可以更新格子的一定比x多,又因为由y更新得到的答案不小于x,所以x在此时此刻就没用了

如果一个节点y入队,则无需条件,但是入队前要弹出队列右端所有符合上文条件的x

在一番弹出后,就能保证队列中的f~i~从队首到队尾递减。

决策时,首先弹掉队首超过范围的点(<i-R)

这时队首点就是决策点。最左边头一个点,因为递减,所以最左边的值最大

依然是老师代码(简化~实测AC~)批注

#include <iostream>
#include <cstdio>
#include <algorithm>
#define inf 0x3f3f3f3f
#define N 200005
using namespace std;
const int INF=0x7f7f7f7f;
int n,L,R,a[N]/*题目输入的四个变量*/,f[N]/*dp数组*/,q[N]/*队列*/,l,r/*滑动窗口范围*/;
int main(){
    cin>>n>>L>>R;//第一行
    for(int i=0;i<=n;i++) {
        scanf("%d",&a[i]);//冰冻指数
    }
    f[0]=a[0];//边界:一开始就在第0个格子,无需移动
    l=0;
    r=-1;
    for(int i=1;i<=n;i++) {
        while(l<=r&&q[l]<i-R) {//q[l]存的格子已经超出i之前R的范围,且队列不为空,可以弹出(因为已经无法对i造成影响)
            l++;//弹出q[l]
        }
        if(i-L>=0) {//保证当前操作的格子编号为非负数
            while(l<=r&&f[i-L]>=f[q[r]]) {//保证队列不空且加入新格子[i-L]后,r不可以更新答案,i-L可以完全替代或者比r更优,弹出所有这样的r
                r--;
            }
            q[++r]=i-L;//加入i-L
        }
        if(l>r) {//队空
            f[i]=-INF;//没有到i的方案
        }
        else {
            f[i]=f[q[l]]+a[i];//队列有元素,用队列最左边的元素更新f[i](不用担心队中其他元素,是金子总会发光,如果某元素有用,一定会留下来,成为下一个l,如果l一直能更新新的格子,那么l会一直待到l<i-R被迫老死(弹出)的那一刻)
        }
    }//到这里,递推循环结束
    int ans=f[n];//开始筛选最大值为答案
    for(int i=n-R+1;i<=n;i++) {
        ans=max(ans,f[i]);//只要“大于”n就算到达对岸,所以最远可能从n-R+1直接跳R格到n+1;最近是从n跳任意k步都能到n+k,又因为n以后的位置没有收益,所以答案应该在n-R+1~n中找
    }
    cout<<ans;
    return 0;
}

p3572 PTA-Little Bird

题目描述

n棵树排成一行,第i棵树高度h~i~。一只鸟想从1跳到n。当它在第i棵树上时,它可以一步跳到 [i+1,i+K]中的任意一棵树。 它从较高的树向较矮的树跳时不消耗体力,否则消耗 1 的体力。 询问 Q 次,每次给定 K,求最小消耗体力。

从1开始,跳到比当前矮的不消耗体力,否则消耗一点体力,每次询问有一个步伐限制,求每次最少耗费多少体力

输入格式(简化)(自己翻译成汉语又翻译回英语)

There is a single integer n (2≤n≤1 000 000)

The second line holds n int d~1~~d~n~ is the height of the trees.

The third line Q (1≤q≤25)

The following Q lines: k~i~(1≤ki≤n?1)

输出格式(简化)

Print q lines,The i~th~ line(the i~th~ bird energy)

题解(批注加粗)

f(i)表示跳到第i棵树消耗的最少体力。dp数组

状态转移方程:f(i)=min(f(j)+(h~j~<h~i~)) (i?K≤j≤i?1)。

如果h~i~(目标树)大那体力就加一,h~j~(起点树)大体力就不变

设x<y,若f(x)>f(y),(无论i的高矮都是f(y)小,和上题类似,y明显比较优)或 f(x)=f(y) 且 h~x~ ≤ h~y~,(这时,如果转移到x,y都能得到一样的f,但是对以后的转移,如果从x到下一棵树消耗体力,那么y到同一棵树不一定消耗体力,y优于x),那么选y不会变差,x没用。这时弹出x

结论队尾为x,新点为 y,如果 f(x) > f(y),或f(x)=f(y)且h~x~≤h~y~, 把 x 弹掉。

所以,是靠队列状态转移到当前状态,和上一题一样

复杂度O(Qn)

老师的代码(简化~实测AC~)+批注

#include <iostream>
#include <cstdio>
#define N 1000010
using namespace std;
int n,Q/*题目给出的n,Q*/,K/*当前鸟的k值,避免浪费数组*/,a[N]/*树高*/,f[N]/*DP*/,q[N]/*队列*/,l,r/*队列左右界*/;
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
    }
    scanf("%d",&Q);
    while(Q--){
        scanf("%d",&K);//本行以上都是读入
        q[0]=1;//第一棵树已经在队内
        l=0;
        r=0;//一开始的队列只有元素0
        for(int i=2;i<=n;i++) {
            while(i-q[l]>K) {//弹出队首的条件:老死(i,l距离已经超过k,鸟跳不过去)
                l++;
            }
            f[i]=f[q[l]]+(a[q[l]]<=a[i]);//同样是用队首更新当前,只要高度上升就使体力加一
            while(l<=r&&(f[i]<f[q[r]]||(f[i]==f[q[r]]&&a[i]>a[q[r]]))) {//条件说明:(队列不空&&(队尾体力大于当前体力||(当前体力和队尾体力相等&&当前高度大于队尾))):这里讨论是否弹出队尾,队列不空是前提,分两种情况符合弹出条件,一、队尾体力消耗已经比当前大了,不可能更新当前。二、队尾体力虽然和当前相等,但是因高度问题,只要到了当前树就会体力+1,也不能更新当前
                r--;//弹出
            }//一通弹出过后,可以保证队列单调递增
            q[++r]=i;//i入队,准备更新下一棵树
        }
        printf("%d\n",f[n]);//f[n]就是答案
    }
    return 0;
}

洛谷网校:动态规划(二)单调队列优化 p1725,p3572

原文:https://www.cnblogs.com/Wild-Donkey/p/12260881.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!