【Luogu P2254】[NOI2005] 瑰丽华尔兹
链接:
题目大意:
一个 \(n\times m\) 的棋盘,有一些格子里有障碍,你在点 \((x,y)\),在 \(K\) 段时间内指定你的方向,你可以不走,求出你最多能走多远。
正文:
考虑用动态规划。设 \(f_{t,x,y}\) 表示第 \(t\) 时刻你在 \((x,y)\) 的最大路程,则有:
\[f_{t,x,y}=\left\{\begin{matrix}\max\{f_{t-1,x-1,y} + 1,f_{t,x,y}\}&(d=1)\\\max\{f_{t-1,x+1,y} + 1,f_{t,x,y}\}&(d=2)\\\max\{f_{t-1,x,y-1} + 1,f_{t,x,y}\}&(d=3)\\\max\{f_{t-1,x,y+1} + 1,f_{t,x,y}\}&(d=4)\end{matrix}\right.
\]
但是因为 \(t\) 太大了,不管怎么优化都会超时,所以我们不能用这个状态那么设 \(f_{K,x,y}\) 表示第 \(K\) 时段你在 \((x,y)\) 的最大路程,则有:
\[f_{K,x,y}=\left\{\begin{matrix}\max_{k\leq t_K-s_K+1}\{f_{K-1,x-k,y} + k,f_{K,x,y}\}&(d=1)\\\max_{k\leq t_K-s_K+1}\{f_{K-1,x+k,y} + k,f_{K,x,y}\}&(d=2)\\\max_{k\leq t_K-s_K+1}\{f_{K-1,x,y-k} + k,f_{K,x,y}\}&(d=3)\\\max_{k\leq t_K-s_K+1}\{f_{K-1,x,y+k} + k,f_{K,x,y}\}&(d=4)\end{matrix}\right.
\]
就可以用单调队列优化它了,又因为转移只包括了相邻项,所以 \(K\) 被滚动掉。
代码:
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ZYC using
#define AK namespace
#define IOI std
#define ll long long
ZYC AK IOI;
const int N = 210;
int n, m, x, y, K;
int s, t, d;
int a[N][N], ans;
int f[N][N], head, tail;
struct node
{
int val, i;
} q[N * N];
int dx[5] = {0, -1, 1, 0, 0},
dy[5] = {0, 0, 0, -1, 1};
void Solve(int x, int y, int d, int len)
{
head = 1, tail = 0;
for (int i = 1; x >= 1 && x <= n && y >= 1 && y <= m; i ++, x += dx[d], y += dy[d])
if (a[x][y]) head = 1, tail = 0;
else
{
while (head <= tail && q[tail].val + i - q[tail].i < f[x][y]) tail--;
q[++tail] = (node) {f[x][y], i};
while (head <= tail && i - q[head].i > len) head++;
f[x][y] = q[head].val + i - q[head].i;
ans = max(ans, f[x][y]);
}
}
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
memset (f, -127 / 3, sizeof f);
scanf ("%d%d%d%d%d", &n, &m, &x, &y, &K);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
char c = getchar();
while (c != '.' && c != 'x') c = getchar();
if (c == '.') a[i][j] = 0;
else a[i][j] = 1;
}
f[x][y] = 0;
for (int k = 1; k <= K; k++)
{
scanf ("%d%d%d", &s, &t, &d);
if (d == 1) for (int i = 1; i <= m; i++) Solve(n, i, d, t - s + 1);
if (d == 2) for (int i = 1; i <= m; i++) Solve(1, i, d, t - s + 1);
if (d == 3) for (int i = 1; i <= n; i++) Solve(i, m, d, t - s + 1);
if (d == 4) for (int i = 1; i <= n; i++) Solve(i, 1, d, t - s + 1);
}
printf ("%d\n", ans);
return 0;
}