首页 > 其他分享 >吔队列了你——写点单调队列优化DP

吔队列了你——写点单调队列优化DP

时间:2022-09-21 21:56:39浏览次数:87  
标签:head DP 队列 int 单调 dp

5_Lei:有没有变态一点的图啊

单调队列优化DP(补)

前言:

DP显然是OI中的一个重要且高频的考点,然而友善的出题人大多不会只考一个推转移方程,往往需要结合一些优化。

单调队列:

看这个的应该都会,不写了,扔个板子上去。

P1886 滑动窗口 /【模板】单调队列

优化DP:

显然,并不是所有的DP都可以用单调队列优化。一般来说,仅有形如

\[dp_{i} = \min/\max_{j = l_{i}}^{i - 1} \left \{ f_{j} \right \} + x \]

且满足 \(l_{i} ≤ l_{i + 1}\) 的DP才能优化。

翻译成人话:对于序列中的每一个点,从其左侧的一段决策区间内的最值进行转移,并且决策区间随着序列区间随着序列下标的增大不断右移。(其实就是一遍滑动窗口)

还是上边的式子,设 \(j <k\),若 \(d_{j}\) 劣于 \(f_{k}\) 的话,那么决策区间移动到 \(k\) 以后,\(j\) 以后永远不会成为最优决策点。

所以维护一个双端队列,满足下标递增,决策性递减。队首即为最优决策点。当队首超出了区间范围,直接出队。同时为了保证单调性,从队尾新加入点前,把队列中比它劣的点从队尾依次出队。

一些例题:

P2254 [NOI2005] 瑰丽华尔兹

给定一个 \(n \times m\) 的矩阵,以 \((x,y)\) 为起点,一共 \(k\) 段时间,每段时间为 \(\left [ s_{i},t_{i} \right ]\),每秒可以向 \(d_{i}\) 方向运动一个单位或不动,且不能超出矩阵,不能走到给出的矩阵的障碍物处,求运动的最长距离。

首先考虑暴力DP,设 \(dp[t][i][j]\) 代表在时刻 \(t\),运动到了 \((i,j)\) 的最长距离,不难得到转移方程为:

\[dp[t][i][j] = max(dp[t][i][j], dp[t - 1][i - dx_{d}][j - dy_{d}] + 1) \]

其中 \(d\) 代表方向,\(dx_{d}\) 代表移动时横坐标的变化量,\(dy_{d}\) 代表移动时纵坐标的变化量。然而它的时空复杂度都高达 \(O(n \times m \times t)\)。

考虑优化,每个时间段只能向一个方向移动,所以按照时间段进行DP。设 \(dp[k][i][j]\) 代表在第 \(k\) 段时间,运动到 \((i,j)\) 的最长距离。

转移方程有:

\[dp[k][i][j] = \max{dp[k - 1][i'][j']} + dis(i,j,i',j') \]

\(i',j'\) 表示上一个合法的位置。并且 \((i,j)\) 和 \((i',j')\) 一定在同一行或者同一列上。

考虑到方程中要求 \(\max\),且求可能拓展的状态有线性关系,所以考虑单调队列。

按照读入的每个时间段的方向,枚举每个方向的起始点。在单调队列里记录所在列或行某一个节点减去它到这一列或行初始位置的距离。对于每个点,用步数(就是初始点到当前点的距离)加上队首。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 210, MAXT = 40010;
const int dx[5] = {0, -1, 1, 0, 0};
const int dy[5] = {0, 0, 0, -1, 1};
int n, m, x, y, k, ans;
char hall[MAXN][MAXN];
int dp[MAXN][MAXN];

struct Ship{
    int st, ed, opt;
}sh[MAXT];

struct Line{
    int x, y, val;
}q[MAXT];

int head = 1, tail = 0;

inline int Get_dis(int ax, int ay, int bx, int by){
    return abs(ax - bx) + abs(ay - by);
}

void Modify(int x, int y, int opt, int len){
    head = 1, tail = 0;

    while(x >= 1 && x <= n && y >= 1 && y <= m){
        if(hall[x][y] == 'x') //撞到家具了,之前搞的作废 
            head = 1, tail = 0;
        else{
            while(head <= tail && q[tail].val + Get_dis(x, y, q[tail].x, q[tail].y) < dp[x][y])
                tail--; //对答案肯定没有贡献,出去 
            q[++tail] = ((Line){x, y, dp[x][y]});
            while(head <= tail &&  max(abs(x - q[head].x), abs(y - q[head].y)) > len)
                head++;
            dp[x][y] = max(dp[x][y], q[head].val + Get_dis(x, y, q[head].x, q[head].y));
            ans = max(ans, dp[x][y]);
        }

        x += dx[opt];
        y += dy[opt];
    }
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read(), m = read(), x = read(), y = read(), k = read();
    for(register int i = 1; i <= n; i++)
        scanf("%s", hall[i] + 1);
    memset(dp, -0x3f, sizeof(dp));
    dp[x][y] = 0;
    for(register int i = 1; i <= k; i++){
        sh[i].st = read(), sh[i].ed = read(), sh[i].opt = read();
        int len = sh[i].ed - sh[i].st + 1;
        if(sh[i].opt == 1)
            for(register int j = 1; j <= m; j++)
                Modify(n, j, sh[i].opt, len);
        else if(sh[i].opt == 2)
            for(register int j = 1; j <= m; j++)
                Modify(1, j, sh[i].opt, len);
        else if(sh[i].opt == 3)
            for(register int j = 1; j <= n; j++)
                Modify(j, m, sh[i].opt, len);
        else if(sh[i].opt == 4)
            for(register int j = 1; j <= n; j++)
                Modify(j, 1, sh[i].opt, len);
    }

    printf("%d", ans);

    return 0;
}

P2569 [SCOI2010]股票交易

P2627 [USACO11OPEN]Mowing the Lawn G

P3089 [USACO13NOV]Pogo-Cow S

CF372C Watching Fireworks is Fun

大家最喜欢的环节:

lei教主昨天提出了重要指导意见,所以今天贯彻落实一下。别问为什么是O,我没图了。

原1
原2
原3

标签:head,DP,队列,int,单调,dp
From: https://www.cnblogs.com/TSTYFST/p/16714293.html

相关文章

  • 使用ThreadPool.SetMinThreads方法提升API服务响应性能的总结
    关于使用ThreadPool.SetMinThreads方法提升API服务响应性能的总结使用该方法的背景?某个API服务在每日请求量40W的情况下,流量增多时会产生大量请求异常:Theoperationwas......
  • 算法-动态规划(DP)
     时间:2022/09/21 一.引入-斐波那契数列下图展示了斐波那契数列数列的递归式:然后我们再看一下在计算fib(7)的时候会出现什么问题:如上图所示,在计算fib(7)的时候......
  • sstate目录改变,导致PetaLinux工程编译出现错误“dpkg-architecture: command not foun
    最近编译PetaLinux工程时,出现错误“dpkg-architecture:commandnotfound”。经过检查,最近移动了本地sstate目录。PetaLinux工程中的sstate的本地目录,已经不存在。恢复......
  • 做题记录整理dp7 P1373 小a和uim之大逃离(2022/9/20)
    P1373小a和uim之大逃离好吧,这道题是看题解的。。。(dp学得是真的拉)不过看的是kksc03这种无代码的,应该也还行。。。#include<bits/stdc++.h>#definefor1(i,a,b)for(......
  • 题目:完成网站的咨询聊天(UDP)
    题目:完成网站的咨询聊天(UDP)学生端:packagecom.gao.Project.Pro6;importjava.io.IOException;importjava.net.*;importjava.util.Scanner;publicclassTestSend......
  • 栈和队列
    栈栈的定义 栈(stack)是限定仅在表尾进行插入和删除操作的线性表。允许插入和删除的一端为栈顶(top),另一端为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出的......
  • 做题记录整理dp3 P1108. 低价购买(2022/9/20)
    P1108.低价购买第一问很明显是一个最长下降子序列第二问就是一个求方案数,有点难想的就是去重感觉这题难度标的有点偏高#include<bits/stdc++.h>#definefor1(i,a,b)......
  • 做题记录整理dp1 P1282. 多米诺骨牌(2022/9/20)
    P1282.多米诺骨牌我们可以把每张骨牌的差值塞进dp的维度了,就变成dpi,j表示前i块骨牌的差值为j的最小旋转次数就可以有递推方程dp[i,j]=max(dp[i-1,j-(a[i]-b[i])],dp[i......
  • 做题记录整理dp1 P1541. [NOIP2010 提高组] 乌龟棋(2022/9/20)
    P1541.[NOIP2010提高组]乌龟棋把每个牌选多少个塞进dp的四个维度里,就可以做到无后效性了#include<bits/stdc++.h>usingnamespacestd;#definefor1(i,a,b)for(ll......
  • 背包dp
    0x0101背包问题题目描述:每个物品最多只能选一次0x02完全背包问题题目描述:每个物品能选无数次,并且每个物品有无限件0x03多重背包问题题目描述:每个物品能选无数......