P2254 [NOI2005] 瑰丽华尔兹
设f[i][x][y]
表示在第i个时段,钢琴在这个时段停止在(x,y)时的最大滑动激励
转移:
dir=1
时 f[i][x][y]=max{f[i-1][x+k][y]+k 其中0<=k<=ed-st+1}
dir=2
时 f[i][x][y]=max{f[i-1][x-k][y]+k 其中0<=k<=ed-st+1}
dir=3
时 f[i][x][y]=max{f[i-1][x][y+k]+k 其中0<=k<=ed-st+1}
dir=4
时 f[i][x][y]=max{f[i-1][x][y-k]+k 其中0<=k<=ed-st+1}
优化:
dir=1
时优化:f[i][x][y]=max{f[i-1][k][y]+k-x 其中x<=k<=x+ed-st+1}
-> 滑动窗口 倒着循环
dir=2
时优化:f[i][x][y]=max{f[i-1][k][y]+x-k 其中x-ed+st-1<=k<=x}
-> 滑动窗口 顺着循环
dir=3
时优化:f[i][x][y]=max{f[i-1][x][k]+k-y 其中y<=k<=y+ed-st+1}
-> 滑动窗口 倒着循环
dir=4
时优化:f[i][x][y]=max{f[i-1][x][k]+y-k 其中y-ed+st-1<=k<=y}
-> 滑动窗口 顺着循环
答案: max{f[k][x][y] 其中1<=x<=n且1<=y<=m}
初始: f[0][start_x][start_y]=0
枚举x(或y)时若是障碍物,则清空队列(因为之前的都没用了)
点击查看代码
// f[i][x][y]为第i段在(x,y)停留的最大滑动距离
// f[i][x][y]=max{f[i-1][x+kdx][y+kdy]+k}:注意,这里的方向是题目中的相反方向
#include <stdio.h> // 使用单调队列优化;如果是障碍物则清空队列
#include <string.h>
#include <algorithm>
const int N = 205, dx[] = {0, -1, 1, 0, 0}, dy[] = {0, 0, 0, -1, 1};
int n, m, stx, sty, k, q[N], hh, tt, l, f[2][N][N]; char g[N][N];
int main() {
scanf("%d%d%d%d%d", &n, &m, &stx, &sty, &k);
for(int i = 1; i <= n; i ++) scanf("%s", g[i] + 1);
memset(f[0], -0x3f, sizeof(f[0])), f[0][stx][sty] = 0;
for(int r = 1, s, t, d; r <= k; r ++) {
memset(f[r&1], -0x3f, sizeof(f[r&1]));
scanf("%d%d%d", &s, &t, &d), l = t - s + 1;
if(d == 1) for(int y = 1; y <= m; y ++) {
hh = 0, tt = -1;
for(int x = n; x; x --)
if(g[x][y] == 'x') hh = 0, tt = -1;
else {
while(hh <= tt && q[hh] > x + l) hh ++;
while(hh <= tt && f[(r-1)&1][x][y] > f[(r-1)&1][q[tt]][y] + q[tt] - x) tt --;
q[++ tt] = x, f[r&1][x][y] = std::max(f[r&1][x][y], f[(r-1)&1][q[hh]][y] + q[hh] - x);
}
} else if(d == 2) for(int y = 1; y <= m; y ++) {
hh = 0, tt = -1;
for(int x = 1; x <= n; x ++)
if(g[x][y] == 'x') hh = 0, tt = -1;
else {
while(hh <= tt && q[hh] < x - l) hh ++;
while(hh <= tt && f[(r-1)&1][x][y] > f[(r-1)&1][q[tt]][y] + x - q[tt]) tt --;
q[++ tt] = x, f[r&1][x][y] = std::max(f[r&1][x][y], f[(r-1)&1][q[hh]][y] + x - q[hh]);
}
} else if(d == 3) for(int x = 1; x <= n; x ++) {
hh = 0, tt = -1;
for(int y = m; y; y --)
if(g[x][y] == 'x') hh = 0, tt = -1;
else {
while(hh <= tt && q[hh] > y + l) hh ++;
while(hh <= tt && f[(r-1)&1][x][y] > f[(r-1)&1][x][q[tt]] + q[tt] - y) tt --;
q[++ tt] = y, f[r&1][x][y] = std::max(f[r&1][x][y], f[(r-1)&1][x][q[hh]] + q[hh] - y);
}
} else for(int x = 1; x <= n; x ++) {
hh = 0, tt = -1;
for(int y = 1; y <= m; y ++)
if(g[x][y] == 'x') hh = 0, tt = -1;
else {
while(hh <= tt && q[hh] < y - l) hh ++;
while(hh <= tt && f[(r-1)&1][x][y] > f[(r-1)&1][x][q[tt]] + y - q[tt]) tt --;
q[++ tt] = y, f[r&1][x][y] = std::max(f[r&1][x][y], f[(r-1)&1][x][q[hh]] + y - q[hh]);
}
}
}
int res = -0x3f3f3f3f;
for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) res = std::max(res, f[k&1][i][j]);
printf("%d\n", res);
return 0;
}