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

题目大意:给定一个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;
}
时间: 2024-10-09 14:33:50

BZOJ 1499 NOI2005 瑰丽华尔兹 单调队列的相关文章

bzoj1499[NOI2005]瑰丽华尔兹 单调队列优化dp

1499: [NOI2005]瑰丽华尔兹 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 1802  Solved: 1097[Submit][Status][Discuss] Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼•布德曼•T.D.•柠檬•1900,朋友们都叫他1900

【BZOJ1499】[NOI2005]瑰丽华尔兹 单调队列+DP

[BZOJ1499][NOI2005]瑰丽华尔兹 Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼•布德曼•T.D.•柠檬•1900,朋友们都叫他1900. 1900在20世纪的第一年出生在往返于欧美的邮轮弗吉尼亚号上,很不幸他刚出生就被抛弃了,成了孤儿.1900孤独的成长在弗吉尼亚号上,从未离开过这个摇晃的世界.也许是对他

wikioi-1748 瑰丽华尔兹 -单调队列优化DP

根据题意,很明显可以推出DP方程. 假如只考虑向左的方向: dp[t][i][j]:  第t个时间段末滑行到i,j最长滑行的距离. dp[t][i][j]=dp[t-1][i][1..k]+(j-k)=dp[t-1][i][1..k]-k+j(k<=j)很明显,可以使用单调队列优化. 最终时间复杂度为O(n*m*k) #include<stdio.h> #include<string.h> #include<algorithm> #include<iostr

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

1499: [NOI2005]瑰丽华尔兹 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 1810  Solved: 1102[Submit][Status][Discuss] Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼?布德曼?T.D.?柠檬?1900,朋友们都叫他1900

NOI2005瑰丽华尔兹

1499: [NOI2005]瑰丽华尔兹 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 893  Solved: 508[Submit][Status] Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼•布德曼•T.D.•柠檬•1900,朋友们都叫他1900. 1900在20世纪

BZOJ1499 [NOI2005]瑰丽华尔兹 【单调队列优化dp】

题目 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼?布德曼?T.D.?柠檬?1900,朋友们都叫他1900. 1900在20世纪的第一年出生在往返于欧美的邮轮弗吉尼亚号上,很不幸他刚出生就被抛弃了,成了孤儿.1900孤独的成长在弗吉尼亚号上,从未离开过这个摇晃的世界.也许是对他命运的补偿,上帝派可爱的小天使艾米丽照顾他.可能是天使的点化,190

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

BZOJ 1012 线段树或单调队列

1012: [JSOI2008]最大数maxnumber 题意:两种操作:1.查询当前数列中末尾L个数中的最大的数:2.当前数列末尾插入一个数. tags:水题 线段树 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i

bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp

Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+1: Line i+1 contains a_i and b_i. Output * Line 1: The maximum possible number of spotted cows on FJ's farm, or -1 if there is no possible solution. S