【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;
}
posted @ 2021-02-14 16:03  Jayun  阅读(23)  评论(0编辑  收藏  举报