BZOJ 1499 瑰丽华尔兹 | 单调队列优化DP

题意

有一块\(n \times m\)的矩形地面,上面有一些障碍(用'#'表示),其余的是空地(用'.'表示)。每时每刻,地面都会向某个方向倾斜,但不同时刻倾斜方向可能不同:具体来说,当时间处于区间\([l_i, r_i]\)内时,向\(d_i\)方向倾斜(输入保证给出的所有区间是连续的)。

海上钢琴师1900的钢琴在这块地面上滑动,每时每刻,可以选择让钢琴向着地面倾斜的方向滑动一格,或是待在原地不动。当然,钢琴不能滑到障碍上,也不能滑出矩形地面之外。问最多能滑动多少路程(进行一次“向某方向滑动一格”这个操作视作增加1单位的路程)。

数据范围:\(n, m, k \le 200\).

题解

首先,这道题是个DP。用\(f[i][x][y]\)表示第i个时间区间结束时(即当前时间是该区间的右端点)以位置\((x, y)\)为终点能走过的最长路程。

四种倾斜方向也对应着四种不同的状态转移方程。以下移为例,状态转移方程是:

\[f[i][x][y] = \max_{j = s}^{x} \{f[i - 1][j][y] + x - j\}
\]

其中,\(s\) = max(x - 时间区间长度, 上一个障碍物的位置 + 1)。

如果直接暴力这样做,复杂度是\(O(n^4)\)的(\(n, m, k\)同阶),显然会TLE。怎么优化呢?

我们发现,所谓对\(f[i - 1][j][y] + x - j\)取max,其实只要对\(f[i - 1][j][y] - j\)取max即可。假如\(j\)的起始点\(s\)不变,那么只需维护一个变量记录\(f[i - 1][j][y] - j\)的最大值、固定从这个最大值转移;但是现在\(s\)随着要求的\(x\)的变化而变化,怎么办呢?注意到\(s\)随着\(x\)的增加也是递增的,可以用一个带有取max功能的队列——单调队列来维护。

所以这道题状态转移部分的伪代码大概是这样的:

将所有距离当前求的\(x\)超过时间区间长度的\(j\)从队首中弹出;

将队尾所有值小于\(f[i - 1][x][y]\)的元素弹出,并将\(f[i-1][x][y] - x\)放到队尾。

(至此所有\(f[i][x][y]\)可以由之转移而来的值已经全在队列中了,且队首的是值最大的、也就是最优的那个。)

用队首元素求出\(f[i][x][y] = f[i - 1][que.front()][y]\) + x。

以上是以“向下倾斜”为例写出的过程,其他三个方向也类似。

以下的内容可以不看。


学姐说:“我这个代码没超过200行……”

于是我决定往死里压行。想压行,就一定要摒弃复制粘贴四次、每段代码稍有不同来处理四个不同倾斜方向的写法!一定要写一个函数!

于是写了下面这样的代码……

#include <cstdio>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 205, _INF = 0xc0c0c0c0;
const int dx[] = {0, -1, 1, 0, 0}, dy[] = {0, 0, 0, -1, 1};
int ans, n, m, q, sx, sy, f[2][N][N], len, que[N], pos[N];
char mp[N][N];
void solve(int i, int x, int y, int dir){
int cnt = 1, ql = 1, qr = 0;
while(x >= 1 && y >= 1 && x <= n && y <= m){
if(mp[x][y] == 'x') ql = 1, qr = 0;
while(ql <= qr && cnt - pos[ql] > len) ql++;
while(ql <= qr && que[qr] + cnt <= f[i ^ 1][x][y]) qr--;
que[++qr] = f[i ^ 1][x][y] - cnt, pos[qr] = cnt;
f[i][x][y] = que[ql] + cnt;
ans = max(ans, f[i][x][y]);
cnt++, x += dx[dir], y += dy[dir];
}
}
int main(){
read(n), read(m), read(sx), read(sy), read(q);
for(int i = 1; i <= n; i++)
scanf("%s", mp[i] + 1);
memset(f, _INF, sizeof(f));
f[0][sx][sy] = 0;
for(int i = 1, st, ed, dir; i <= q; i++){
read(st), read(ed), read(dir), len = ed - st + 1;
if(dir == 1) for(int j = 1; j <= m; j++) solve(i & 1, n, j, dir);
if(dir == 2) for(int j = 1; j <= m; j++) solve(i & 1, 1, j, dir);
if(dir == 3) for(int j = 1; j <= n; j++) solve(i & 1, j, m, dir);
if(dir == 4) for(int j = 1; j <= n; j++) solve(i & 1, j, 1, dir);
}
write(ans), enter;
return 0;
}

BZOJ 1499 [NOI2005] 瑰丽华尔兹 | 单调队列优化DP的更多相关文章

  1. bzoj 1499 [NOI2005]瑰丽华尔兹——单调队列优化dp

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1499 简单的单调队列优化dp.(然而当时却WA得不行.今天总算填了坑) 注意滚动数组赋初值应 ...

  2. bzoj1499[NOI2005]瑰丽华尔兹 单调队列优化dp

    1499: [NOI2005]瑰丽华尔兹 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 1802  Solved: 1097[Submit][Status ...

  3. BZOJ 1499 NOI2005 瑰丽华尔兹 单调队列

    题目大意:给定一个m*n的地图,一些点有障碍物,钢琴初始在一个点,每一个时间段能够选择向给定的方向移动一段距离,求最长路径长 朴素DP的话,我们有T个时间段,每一个时间段有m*n个点,n个时间,一定会 ...

  4. bzoj1499 [NOI2005]瑰丽华尔兹——单调队列优化DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1499 朴素DP方程很好想,以右移为例,就是 f[i][x][y]=max(f[i][x][y ...

  5. ●BZOJ 1499 [NOI2005]瑰丽华尔兹

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1499 题解: 单调队列优化DP 定义 dp[t][x][y] 表示第t个时间段之后,处在(x ...

  6. 【BZOJ1499】[NOI2005]瑰丽华尔兹 单调队列+DP

    [BZOJ1499][NOI2005]瑰丽华尔兹 Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是 ...

  7. BZOJ 3126 [USACO2013 Open]Photo (单调队列优化DP)

    洛谷传送门 题目大意:给你一个长度为$n$的序列和$m$个区间,每个区间内有且仅有一个1,其它数必须是0,求整个序列中数字1最多的数量 神题,竟然是$DP$ 定义$f_{i}$表示第i位放一个1时,最 ...

  8. bzoj 3831 Little Bird (单调队列优化dp)

    /*先贴个n*n的*/ #include<iostream> #include<cstdio> #include<cstring> #define maxn 100 ...

  9. bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp

    Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+ ...

随机推荐

  1. MySQL基础架构之查询语句执行流程

    这篇笔记主要记录mysql的基础架构,一条查询语句是如何执行的. 比如,在我们从student表中查询一个id=2的信息 select * from student where id=2; 在解释这条 ...

  2. 教你如何编写、保存与运行 Python 程序

    第一步 接下来我们将看见如何在 Python 中运行一个传统的“Hello World”程序.Python教程本章将会教你如何编写.保存与运行 Python 程序. 通过 Python 来运行的你的程 ...

  3. GsonFormat插件主要用于使用Gson库将JSONObject格式的String 解析成实体,该插件可以加快开发进度,使用非常方便,效率高。

    GsonFormat插件主要用于使用Gson库将JSONObject格式的String 解析成实体,该插件可以加快开发进度,使用非常方便,效率高. 插件地址:https://plugins.jetbr ...

  4. 【LeetCode算法题库】Day3:Reverse Integer & String to Integer (atoi) & Palindrome Number

    [Q7]  把数倒过来 Given a 32-bit signed integer, reverse digits of an integer. Example 1: Input: 123 Outpu ...

  5. 如何在window服务器上搭建一个能代替ftp的传输工具

    通常对于服务器上的文件管理和数据传输都是利用ftp来实现,但随着存储技术的发展,数据资产的存储规模和复杂程度不断提高,传统的ftp传输显得有笨重.今天给大家介绍一款能够取代ftp的在线文档管理软件—— ...

  6. Docker持久化存储与数据共享

    一.Docker持久化数据的方案 基于本地文件系统的Volume:可以在执行docker create或docker run时,通过-v参数将主机的目录作为容器的数据卷.这部分功能便是基于本地文件系统 ...

  7. Hyperledger Fabric(v1.1.0)编译时遇到的问题

    Hyperledger Fabric(v1.1.0)编译时遇到的问题 0. 编译过程的坑 编译时,按照如下顺序编译 make release,编译源码生成二进制文件 make docker,生成一系列 ...

  8. 配置Ubuntu16.04虚拟机 (用途:CTF_pwn)

    因为学习需要16.xx的虚拟机,所以把之前18.04的Ubuntu卸掉重装了一遍Ubuntu16.04, 考虑到我有备份和重装系统的爱好,故记之,以备后用. 目录: //最后更新时间:190122·1 ...

  9. JDK自带的监控工具方法

    一.概述       SUN 的JDK中的几个工具,非常好用.秉承着有免费,不用商用的原则.以下简单介绍一下这几种工具.(注:本文章下的所有工具都存在JDK5.0以上版本的工具集里(jdk的bin目录 ...

  10. 配置Tomcat使用HTTP/2

    转自: https://zhuanlan.zhihu.com/p/21349186 前情提要: Tomcat高效响应的秘密(一) Sendfile与Gzip Tomcat高效响应的秘密(二) keep ...