BZOJ1499 单调队列+DP

1499: [NOI2005]瑰丽华尔兹

Time Limit: 3 Sec  Memory Limit: 64 MB
Submit: 1560  Solved: 949
[Submit][Status][Discuss]

Description

你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐。但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼?布德曼?T.D.?柠檬?1900,朋友们都叫他1900。 1900在20世纪的第一年出生在往返于欧美的邮轮弗吉尼亚号上,很不幸他刚出生就被抛弃了,成了孤儿。1900孤独的成长在弗吉尼亚号上,从未离开过这个摇晃的世界。也许是对他命运的补偿,上帝派可爱的小天使艾米丽照顾他。可能是天使的点化,1900拥有不可思议的钢琴天赋:从未有人教,从没看过乐谱,但他却能凭着自己的感觉弹出最沁人心脾的旋律。当1900的音乐获得邮轮上所有人的欢迎时,他才8岁,而此时的他已经乘着海轮往返欧美大陆50余次了。虽说是钢琴奇才,但1900还是个孩子,他有着和一般男孩一样的好奇和调皮,只不过更多一层浪漫的色彩罢了:这是一个风雨交加的夜晚,海风卷起层层巨浪拍打着弗吉尼亚号,邮轮随着巨浪剧烈的摇摆。船上的新萨克斯手马克斯?托尼晕船了,1900招呼托尼和他一起坐上舞厅里的钢琴,然后松开了固定钢琴的闸,于是,钢琴随着海轮的倾斜滑动起来。准确的说,我们的主角1900、钢琴、邮轮随着1900的旋律一起跳起了华尔兹,随着“嘣嚓嚓”的节奏,托尼的晕船症也奇迹般的消失了。后来托尼在回忆录上写道:大海摇晃着我们使我们转来转去快速的掠过灯和家具我意识到我们正在和大海一起跳舞真是完美而疯狂的舞者晚上在金色的地板上快乐的跳着华尔兹是不是很惬意呢?也许,我们忘记了一个人,那就是艾米丽,她可没闲着:她必须在适当的时候施展魔法帮助1900,不让钢琴碰上舞厅里的家具。不妨认为舞厅是一个N行M列的矩阵,矩阵中的某些方格上堆放了一些家具,其他的则是空地。钢琴可以在空地上滑动,但不能撞上家具或滑出舞厅,否则会损坏钢琴和家具,引来难缠的船长。每个时刻,钢琴都会随着船体倾斜的方向向相邻的方格滑动一格,相邻的方格可以是向东、向西、向南或向北的。而艾米丽可以选择施魔法或不施魔法:如果不施魔法,则钢琴会滑动;如果施魔法,则钢琴会原地不动。艾米丽是个天使,她知道每段时间的船体的倾斜情况。她想使钢琴在舞厅里滑行路程尽量长,这样1900会非常高兴,同时也有利于治疗托尼的晕船。但艾米丽还太小,不会算,所以希望你能帮助她。

Input

输入文件的第一行包含5个数N, M, x, y和K。N和M描述舞厅的大小,x和y为钢琴的初始位置(x行y列);我们对船体倾斜情况是按时间的区间来描述的,且从1开始计量时间,比如“在[1, 3]时间里向东倾斜,[4, 5]时间里向北倾斜”,因此这里的K表示区间的数目。以下N行,每行M个字符,描述舞厅里的家具。第i行第j列的字符若为‘ . ’,则表示该位置是空地;若为‘ x ’,则表示有家具。以下K行,顺序描述K个时间区间,格式为:si ti di。表示在时间区间[si, ti]内,船体都是向di方向倾斜的。di为1, 2, 3, 4中的一个,依次表示北、南、西、东(分别对应矩阵中的上、下、左、右)。输入保证区间是连续的,即 s1 = 1 si = ti-1 + 1 (1 < i ≤ K) tK = T

Output

输出文件仅有1行,包含一个整数,表示钢琴滑行的最长距离(即格子数)。

Sample Input

4 5 4 1 3

..xx.

.....

...x.

.....

1 3 4

4 5 1

6 7 3

Sample Output

6

HINT

分析:

令 f(k,x,y)=此人 k 次滑行后到达(x,y)方格时已经滑行的最长距离。动态规划的状态转移方程如下(以下仅给出向东滑行的状态转移方程,其他 3 个方向上的转移方程可以 类似地推出):f(0,startx,starty)=0,f(k,x,y)=max{f(k-1,x,y),f(k-1,x,y-1)+1,f(k-1,x,y-2)+2,......,f(k-1,x,y’)+y-y’} (其中 y’为满足 y=1 或(x,y’-1)上有障碍或 y’=y-ck 的最大值),

对于一个具体的例子 k=2,x=1,c2=2 可以列出如下等式: f(2,1,1)=max{f(1,1,1)}

f(2,1,2)=max{f(1,1,1)+1,f(1,1,2)}
f(2,1,3)=max{f(1,1,1)+2,f(1,1,2)+1,f(1,1,3)}
f(2,1,4)=max{f(1,1,2)+2,f(1,1,3)+1,f(1,1,4)}

......

如果我们定义一个序列 a,使得 ai=f(1,1,i)-i+1,则以上等式可以写成:
f(2,1,1)=max{a1}=max{a1}

f(2,1,2)=max{a1+1,a2+1}=max{a1,a2}+1
f(2,1,3)=max{a1+2,a2+2,a3+2}=max{a1,a2,a3}+2
f(2,1,4)=max{a1+3,a2+3,a3+3,a4+3}=max{a2,a3,a4}+3

......

显然,在应用了 a 序列之后,我们就可以只关注 a 序列而不必为每个 ai 加上一个不

同的值,从而简化了操作。

前面已经对于序列的插入与删除进行过讨论。其中只在一端插入,另一端删除的特 性恰好符合队列的性质。但是,这里是要求队列中所有数的最大值,普通的队列可以胜 任这个操作吗?让我们首先来分析一下如何存储队列中的数。

如图 16 所示,对于已经出现在队列中的 a2 与 a3,如果 a2≤a3,则 a2 是没有必要出 现在队列中的。因为根据队列的插入与删除原则可以推导出,如果队列中已经出现 a3 了,则在 a2 被删除之前,a3 是一定不会被删除的。因此,a2 与 a3 会一直同时出现在队 列中,直至 a2 被删除。但是 a2≤a3,因此队列中的最大值永远不会是 a2,也就没有必要 存储 a2.

根据这一条重要的性质,可以立刻推导出“有必要”存储在队列中的 a 值的大小关 系——严格递减。也就是说,存储在队列中的 a 值是依次减小的,而队头元素的值为最 大值,也就是当前队列中所有数字(无论是否存储在队列中)的最大值。这样,每次可 以取出位于队头的 a 值作为最大值。但是一个新的问题摆在我们面前——如何实时维护 队列,即如何正确地插入或删除元素。

首先研究删除操作。很显然,如果队头 a 值的下标对应的方格与当前处理的方格之 间的距离已经大于 ck,则直接将它从队列中删除,即队头指针加一。

接着是插入操作。根据前文中对于队列中元素大小关系的讨论可以得知,插入一个 元素 ai 后,队列中不能有元素 aj 满足 aj≤ai. 于是,我们可以从队尾开始,依次删除掉不 大于 ai 的 a 值,直到队列中剩下的元素都大于 ai. 此时就可以将 ai 插入队尾。(插入与 删除过程见图 17)。

很显然,每次求队列中所有元素的最大值可以直接查看队头元素。根据此队列性质, 队头元素一定是队列中所有元素里最大的一个。

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "string"
 5 #include "algorithm"
 6 using namespace std;
 7 const int maxn=200+10;
 8 char g[maxn][maxn];
 9 int f[maxn][maxn];
10 int n,m,x,y,k,l,r,res;
11 pair<int,int> q[maxn],tmp;
12 int dx[]={0,-1,1,0,0},dy[]={0,0,0,-1,1};
13 bool judge(int x,int y){
14     if(x>=1&&x<=n&&y>=1&&y<=m)  return true;
15     return false;
16 }
17 void solve(int x,int y,int d,int len){
18     l=r=0;
19     for(int i=0;judge(x,y);i++,x+=dx[d],y+=dy[d]){
20         if(g[x][y]==‘x‘)   l=r=0;
21         else{
22             tmp.first=f[x][y],tmp.second=i;
23             while(l<r&&q[r-1].first+(i-q[r-1].second)<=tmp.first)  r--;
24             q[r++]=tmp;
25             while(l<r&&(i-q[l].second)>len)  l++;
26             f[x][y]=q[l].first+(i-q[l].second);
27             res=max(res,f[x][y]);
28         }
29     }
30 }
31 int main()
32 {
33     scanf("%d%d%d%d%d",&n,&m,&x,&y,&k);
34     for(int i=1;i<=n;i++)
35         scanf("%s",g[i]+1);
36     memset(f,0x80,sizeof(f)); f[x][y]=0;
37     for(int i=1,s,e,d,len;i<=k;i++){
38         scanf("%d%d%d",&s,&e,&d); len=e-s+1;
39         if(d==1)  for(int j=1;j<=m;j++)  solve(n,j,1,len);
40         else if(d==2)  for(int j=1;j<=m;j++)  solve(1,j,2,len);
41         else if(d==3)  for(int j=1;j<=n;j++)  solve(j,m,3,len);
42         else for(int j=1;j<=n;j++)   solve(j,1,4,len);
43     }
44     printf("%d\n",res);
45 }

详见2006朱晨光《基本数据结构在信息学竞赛当中的应用》

时间: 2024-08-10 23:29:58

BZOJ1499 单调队列+DP的相关文章

POJ 3017 单调队列dp

Cut the Sequence Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8764   Accepted: 2576 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subseque

[TyvjP1313] [NOIP2010初赛]烽火传递(单调队列 + DP)

传送门 就是个单调队列+DP嘛. ——代码 1 #include <cstdio> 2 3 const int MAXN = 1000001; 4 int n, m, h = 1, t = 1, ans = ~(1 << 31); 5 int q[MAXN], a[MAXN], f[MAXN]; 6 7 inline int min(int x, int y) 8 { 9 return x < y ? x : y; 10 } 11 12 int main() 13 { 14

hdu4374单调队列+dp

http://acm.hdu.edu.cn/showproblem.php?pid=4374 Problem Description Now there is a game called the new man down 100th floor. The rules of this game is: 1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part

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

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

UVA Live Achrive 4327 Parade (单调队列,dp)

容易想到dp[i][j]表示在第i行j个路口的开始走最大高兴值. 每次可以向左走,或者向右边走,然后向北走.(或者直接往北) 向左走到,状态转移为dp[i][j] = dp[i][k] + happy[i][k][j](为了方便处理,i从1开始编号,0行dp值存0) 处理出前缀和,happy[i][k][j]表示为sum[i][j] - sum[i][k] 向左走应该取max(dp[i][k]-sum[i][k]) k应该满足time[i][k][j] <= k 随着j的变化k的范围是一个滑动窗

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

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

vijos P1243 生产产品(单调队列+DP)

P1243生产产品 描述 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产 品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N 台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的 时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机

BZOJ 3831 POI 2014 Little Bird 单调队列DP

题目大意:给出一片树林,树排成一排,每一棵树都有一个高度.从地一棵树出发,每次可以跳到i+k棵之前,跳到小于自己高度的树上不需要花费体力,反之需要花费一点体力,问到最后一棵树最少需要多少体力. 思路:简单DP方程:f[i] = min{f[j] + (height[i] >= height[j])} 然后发现数据范围只有O(n)可以过. 维护单调队列,队列中按照f单调递减,队尾按照时间往出弹. 当f值相同的时候,高度较高的优先. CODE: #include <queue> #inclu

codeforces 1077F2. Pictures with Kittens (hard version)单调队列+dp

div3挑战一场蓝,大号给基佬紫了,结果从D开始他开始疯狂教我做人??表演如何AKdiv3???? 比赛场上:A 2 分钟,B题蜜汁乱计数,结果想得绕进去了20多分钟,至此GG,C秒出,D...还在想怎么做就告诉我二分答案,好那继续秒出.他看F我看E,没思路互换,F题都读了半天,其实就是我YY的题意,然后发现还是没N^3dp的想法,发现并不是区间dp,其实之前我E想到了枚举等差数列的项数,只是发现找不了起点(那不是找终点就好了).基佬紫:枚举项数啊,我????然后他写完我就发现自己又傻了,rus