[NOI2005]瑰丽华尔兹 && dp单调队列

本文介绍了一种结合裸DP和单调队列的算法,用于解决区间问题。通过手写双端队列并注意数组操作细节,实现算法优化。详细解释了每个步骤及关键点,包括初始化、迭代过程和边界处理。此方法适用于时间区间内求最大值等类似场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

裸DP (知道要用单调队列的前提下)按照时间区间做DP 单调队列也没什么特别的 就是手写双端队列要注意清0 以及各种数组加减号有点容易打错略坑

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 200
char Map[MAXN+10][MAXN+10];
int dp[MAXN+10][MAXN+10][MAXN+10];
int n, m, K;
struct node{
	int st, ed, dir;
}A[MAXN+10];
int Q[MAXN*10+10];
int F, R;
int main()
{
	int stx, sty;
	scanf("%d%d%d%d%d", &n, &m, &stx, &sty, &K);
	for(int i = 1; i <= n; i++) scanf("%s", Map[i]+1);
	memset(dp, ~0x3f, sizeof(dp));
	dp[0][stx][sty] = 0;
	for(int i = 1; i <= K; i++) scanf("%d%d%d", &A[i].st, &A[i].ed, &A[i].dir);
	
	for(int d = 1; d <= K; d++)
	{
		int T = A[d].ed - A[d].st + 1, dir = A[d].dir;
		
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= m; j++)
				dp[d][i][j] = dp[d-1][i][j];

		if(dir == 1)
			for(int j = 1; j <= m; j++)
			{
				F = R = Q[0] = 0;
				memset(Q, 0, sizeof(Q));
				for(int i = n; i >= 1; i--)
				{
					if(Map[i][j] == 'x') { F = R = Q[0] = 0; continue; }
					while(F < R && Q[F] - i > T) F++;
					dp[d][i][j] = max(dp[d][i][j], dp[d-1][Q[F]][j] + Q[F] - i);
					while(F < R && dp[d-1][i][j] + i >= dp[d-1][Q[R-1]][j] + Q[R-1]) R--;
					Q[R++] = i;
				}
			}
		
		if(dir == 2)
			for(int j = 1; j <= m; j++)
			{
				F = R = Q[0] = 0;
				memset(Q, 0, sizeof(Q));
				for(int i = 1; i <= n; i++)
				{
					if(Map[i][j] == 'x') { F = R = Q[0] = 0; continue; }
					while(F < R && i - Q[F] > T) F++;
					dp[d][i][j] = max(dp[d][i][j], dp[d-1][Q[F]][j] + i - Q[F]);
					while(F < R && dp[d-1][i][j] - i >= dp[d-1][Q[R-1]][j] - Q[R-1]) R--;
					Q[R++] = i;
				}
			}
		
		if(dir == 3)
			for(int i = 1; i <= n; i++)
			{
				F = R = Q[0] = 0;
				memset(Q, 0, sizeof(Q));
				for(int j = m; j >= 1; j--)
				{
					if(Map[i][j] == 'x') { F = R = Q[0] = 0; continue; }
					while(F < R && Q[F] - j > T) F++;
					dp[d][i][j] = max(dp[d][i][j], dp[d-1][i][Q[F]] + Q[F] - j);
					while(F < R && dp[d-1][i][j] + j >= dp[d-1][i][Q[R-1]] + Q[R-1]) R--;
					Q[R++] = j;
				}
			}
		
		if(dir == 4)
			for(int i = 1; i <= n; i++)
			{
				F = R = Q[0] = 0;
				for(int j = 1; j <= m; j++)
				{
					if(Map[i][j] == 'x') { F = R = Q[0] = 0; continue; }
					while(F < R && j - Q[F] > T) F++;
					dp[d][i][j] = max(dp[d][i][j], dp[d-1][i][Q[F]] - Q[F] + j);
					while(F < R && dp[d-1][i][j] - j >= dp[d-1][i][Q[R-1]] - Q[R-1]) R--;
					Q[R++] = j;
				}
			}
	}
	int Ans = -1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            Ans = max(Ans, dp[K][i][j]);
    printf("%d", Ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值