[NOI2005]瑰丽华尔兹

嘟嘟嘟



这题大家应该都做过,就是暴力dp+单调队列优化。



dp方程其实很好想,最初是这样的:dp[t][i][j]表示时刻\(t\)后,走到\((i, j)\)格子的最远路程,于是就有:

\[dp[t][i][j] = max\{ dp[t - 1][px][py] \} + 1
\]

但这是\(O(Tn ^ 2)\)的,不仅会TLE,还能MLE。



接着看题,发现给得\(K\)没用上。想一下发现\(K\)的特点是同一时间区间的移动方向是一样的,于是我们把第一维改成第\(k\)个时间区间,转移方程就变成了:

\[dp[k][i][j] = max_{h = 1} ^ {len}\{ dp[k - 1][i - dx[d]][j - dy[d]] + dis(i, j, i - dx[d], j - dy[d])\}
\]

\(len\)表示区间长度。

方程可能丑了点,但意思就是枚举这个点能在这个时间段内从哪儿转移过来,然后就是对应的dp值加上这两点之间的距离。

注意如果有的格子不能走,就不能从这里转移。

复杂度\(O(kn ^ 3)\)。



于是就有单调队列优化啦。

对于一个点,这一步能转移到他的实际上就是x或y方向上连续的一段dp值,于是我们把这些dp值放进单调队列里就行了。

这样优化到\(O(kn ^ 2)\),就过了。

需要注意的是,我们要用k - 1时刻更新k时刻的答案,因此先把dp[x][y]放入队列,再用队首更新dp值。这样就保证了放进去的dp值一定是上一个时刻的。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 205;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} int n, m, sx, sy, K;
char a[maxn][maxn];
struct Node
{
int L, R, dir;
}t[maxn];
int dp[maxn][maxn]; struct Que
{
int val, x, y;
}q[maxn];
int l = 1, r = 0;
const int dx[] = {0, -1, 1, 0, 0}, dy[] = {0, 0, 0, -1, 1};
In bool check(int x, int y) {return x && x <= n && y && y <= m;}
In int dis(int xa, int ya, int xb, int yb) {return abs(xa - xb) + abs(ya - yb);}
In void solve(int x, int y, int d, int len)
{
l = 1; r = 0;
while(check(x, y))
{
if(a[x][y] == 'x') l = 1, r = 0;
else
{
while(l <= r && q[r].val + dis(x, y, q[r].x, q[r].y) < dp[x][y]) --r;
q[++r] = (Que){dp[x][y], x, y};
while(l <= r && dis(x, y, q[l].x, q[l].y) > len) ++l;
dp[x][y] = q[l].val + dis(x, y, q[l].x, q[l].y);
}
x += dx[d]; y += dy[d];
}
} int main()
{
n = read(); m = read(); sx = read(), sy = read(), K = read();
for(int i = 1; i <= n; ++i) scanf("%s", a[i] + 1);
for(int i = 1; i <= K; ++i) t[i].L = read(), t[i].R = read(), t[i].dir = read();
Mem(dp, -0x3f); dp[sx][sy] = 0;
for(int i = 1; i <= K; ++i)
{
if(t[i].dir == 1) for(int j = 1; j <= m; ++j) solve(n, j, t[i].dir, t[i].R - t[i].L + 1);
if(t[i].dir == 2) for(int j = 1; j <= m; ++j) solve(1, j, t[i].dir, t[i].R - t[i].L + 1);
if(t[i].dir == 3) for(int j = 1; j <= n; ++j) solve(j, m, t[i].dir, t[i].R - t[i].L + 1);
if(t[i].dir == 4) for(int j = 1; j <= n; ++j) solve(j, 1, t[i].dir, t[i].R - t[i].L + 1);
}
int ans = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) ans = max(ans, dp[i][j]);
write(ans), enter;
}
上一篇:Android 7.0以上版本 系统解决拍照的问题 exposed beyond app through ClipData.Item.getUri()


下一篇:Java计算两个日期相差的天数