「NOI 2005」瑰丽华尔兹
作者:互联网
题目大意:给定一个 $n times m$ 的棋盘,起点为 $(x,y)$,给定 $k$ 个时间段,每个时间段给出一个移动的方向,每次可以往当前指定的方向移动一步或停在原地,不能撞到障碍物或走出棋盘,问最多能走多少步。$n,m,k leq 200$
设 $f[k][i][j]$ 表示第 $k$ 个时间段结束时走到 $(i,j)$ 的最长滑动距离,无法到达表示为 $−infty$,设 $len=t_k−s_k+1$,即第 $k$ 个时间段的持续时间。
以向右走为例,有状态转移方程
单调队列维护 $t$ 单调递增,$f$ 单调递减。
#include <cstdio>
const int N = 205, INF = 0x7fffffff;
const int dx[5] = {0, -1, 1, 0, 0};
const int dy[5] = {0, 0, 0, -1, 1};
char a[N][N]; int n, m, f[N][N], q[N][2], ans;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
int max(int x, int y) {
return x > y ? x : y;
}
void solve(int x, int y, int len, int d) {
int head = 1, tail = 0;
for (int i = 1; x >= 1 && x <= n && y >= 1 && y <= m; x += dx[d], y += dy[d], ++i) {
if (a[x][y] == 'x') head = 1, tail = 0;
else {
while (head <= tail && q[tail][1] + i - q[tail][0] < f[x][y]) --tail;
q[++tail][0] = i, q[tail][1] = f[x][y];
if (q[tail][0] - q[head][0] > len) ++head;
f[x][y] = q[head][1] + i - q[head][0];
ans = max(ans, f[x][y]);
}
}
}
int main() {
n = read(), m = read(); int x = read(), y = read(), k = read();
for (int i = 1; i <= n; ++i) scanf("%s", a[i] + 1);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) f[i][j] = -INF;
f[x][y] = 0;
while (k--) {
int s = read(), t = read(), d = read();
if (d == 1) for (int i = 1; i <= m; ++i) solve(n, i, t - s + 1, d); //up
if (d == 2) for (int i = 1; i <= m; ++i) solve(1, i, t - s + 1, d); //down
if (d == 3) for (int i = 1; i <= n; ++i) solve(i, m, t - s + 1, d); //left
if (d == 4) for (int i = 1; i <= n; ++i) solve(i, 1, t - s + 1, d); //right
}
printf("%dn", ans);
return 0;
}
时间复杂度 $O(nmk)$
标签:华尔兹,head,NOI,int,ans,read,时间段,&&,2005 来源: https://www.cnblogs.com/petewell/p/11611534.html