BZOJ 1499 NOI2005 瑰丽华尔兹 单调队列

27 篇文章 0 订阅
13 篇文章 1 订阅

题目大意:给定一个m*n的地图,一些点有障碍物,钢琴初始在一个点,每个时间段可以选择向给定的方向移动一段距离,求最长路径长

朴素DP的话,我们有T个时间段,每个时间段有m*n个点,n个时间,一定会超时

考虑到一个时间段所有的更新操作都是相同的,我们可以考虑单调队列优化

设队尾为(x,y),新插入的点为(x',y'),那么当Distance( (x,y) , (x',y') ) <= f[x'][y'] - f[x][y]时,(x,y)可被删掉

四遍单调队列即可 O(T*m*n)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 210
using namespace std;
typedef pair<int,int> abcd;
int n,m,k,ans;
char map[M][M];
int f[M][M],g[M][M];
abcd q[M];int r,h;
inline int Distance(const abcd &x,const abcd &y)
{
	return abs(x.first-y.first)+abs(x.second-y.second);
}
inline void Insert(const abcd &x)
{
	while( r!=h && Distance(x,q[r]) <= f[x.first][x.second] - f[q[r].first][q[r].second] )
		--r;
	q[++r]=x;
}
inline int Get_Ans(const abcd &x,int len)
{
	while( r!=h && Distance(q[h+1],x)>len )
		++h;
	if(r==h)
		return 0xefefefef;
	return f[q[h+1].first][q[h+1].second]+Distance(q[h+1],x);
}
void U(int len)
{
	int i,j;
	for(j=1;j<=n;j++)
	{
		r=h=0;
		for(i=m;i;i--)
			if(map[i][j]=='.')
			{
				abcd p(i,j);
				g[i][j]=max( f[i][j] , Get_Ans(p,len) );
				Insert(p);
			}
			else
				r=h=0;
	}
	memcpy(f,g,sizeof f);
}
void D(int len)
{
	int i,j;
	for(j=1;j<=n;j++)
	{
		r=h=0;
		for(i=1;i<=m;i++)
			if(map[i][j]=='.')
			{
				abcd p(i,j);
				g[i][j]=max( f[i][j] , Get_Ans(p,len) );
				Insert(p);
			}
			else
				r=h=0;
	}
	memcpy(f,g,sizeof f);
}
void L(int len)
{
	int i,j;
	for(i=1;i<=m;i++)
	{
		r=h=0;
		for(j=n;j;j--)
			if(map[i][j]=='.')
			{
				abcd p(i,j);
				g[i][j]=max( f[i][j] , Get_Ans(p,len) );
				Insert(p);
			}
			else
				r=h=0;
	}
	memcpy(f,g,sizeof f);
}
void R(int len)
{
	int i,j;
	for(i=1;i<=m;i++)
	{
		r=h=0;
		for(j=1;j<=n;j++)
			if(map[i][j]=='.')
			{
				abcd p(i,j);
				g[i][j]=max( f[i][j] , Get_Ans(p,len) );
				Insert(p);
			}
			else
				r=h=0;
	}
	memcpy(f,g,sizeof f);
}
int main()
{
	int i,j,x,y,z;
	cin>>m>>n>>x>>y>>k;
	for(i=1;i<=m;i++)
		scanf("%s",map[i]+1);
	memset(f,0xef,sizeof f);
	memset(g,0xef,sizeof g);
	f[x][y]=0;
	for(i=1;i<=k;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		switch(z)
		{
			case 1:U(y-x+1);break;
			case 2:D(y-x+1);break;
			case 3:L(y-x+1);break;
			case 4:R(y-x+1);break;
		}
	}
	for(i=1;i<=m;i++)
		for(j=1;j<=n;j++)
			ans=max(ans,f[i][j]);
	cout<<ans<<endl;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值