【Luogu P2254】[NOI2005] 瑰丽华尔兹

链接:

题目

题目大意:

一个 (n imes m) 的棋盘,有一些格子里有障碍,你在点 ((x,y)),在 (K) 段时间内指定你的方向,你可以不走,求出你最多能走多远。

正文:

考虑用动态规划。设 (f_{t,x,y}) 表示第 (t) 时刻你在 ((x,y)) 的最大路程,则有:

[f_{t,x,y}=left{egin{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} ight. ]

但是因为 (t) 太大了,不管怎么优化都会超时,所以我们不能用这个状态那么设 (f_{K,x,y}) 表示第 (K) 时段你在 ((x,y)) 的最大路程,则有:

[f_{K,x,y}=left{egin{matrix}max_{kleq t_K-s_K+1}{f_{K-1,x-k,y} + k,f_{K,x,y}}&(d=1)\max_{kleq t_K-s_K+1}{f_{K-1,x+k,y} + k,f_{K,x,y}}&(d=2)\max_{kleq t_K-s_K+1}{f_{K-1,x,y-k} + k,f_{K,x,y}}&(d=3)\max_{kleq t_K-s_K+1}{f_{K-1,x,y+k} + k,f_{K,x,y}}&(d=4)end{matrix} ight. ]

就可以用单调队列优化它了,又因为转移只包括了相邻项,所以 (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
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/GJY-JURUO/p/14402086.html