【bzoj1499】[NOI2005]瑰丽华尔兹(dp+单调队列优化)

来源:互联网 发布:华为云计算培训 编辑:程序博客网 时间:2024/05/21 02:36

题目:

我是超链接

题解:一个很显然的O(nmT)的做法就是令f[t][i][j]表示时刻t时,钢琴位于(i,j)处时,从时刻1到t的最长滑行路程。很容易得到DP方程 

f[t][i][j]=max{f[t−1][i][j],f[t−1][i'][j']+1}

TTTT啦,我们可以用单调队列优化,由于钢琴的移动方向是在一段一段的时间内是相同的,因此可以考虑成段dp。

令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’}

对于一个具体的例子 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}+1f(2,1,3)=max{a1+2,a2+2,a3+2}=max{a1,a2,a3}+2f(2,1,4)=max{a1+3,a2+3,a3+3,a4+3}=max{a2,a3,a4}+3
......
显然,在应用了 a 序列之后,我们就可以只关注 a 序列而不必为每个 ai 加上一个不同的值,从而简化了操作。

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

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

代码:

#include <cstdio>#include <cstring>#include <iostream>#define INF 1e9+7using namespace std;int c[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}};pair <int,int> q[205],tmp;int belong[250],f[205][205],n,m,ans,l,r;int x,y,k,i,j;char st[250][250];bool judge(int x,int y){if (x>=1 && y>=1 && x<=n && y<=m) return true;return false;}void work(int x,int y,int d,int len){l=r=0;for (int i=0;judge(x,y);i++,x+=c[d][0],y+=c[d][1])  if (st[x][y]=='x') l=r=0;  else  {  tmp.first=f[x][y]; tmp.second=i;//崭新程度  while (l<r && q[r-1].first + (i-q[r-1].second)<=tmp.first) r--;  q[r++]=tmp;  while (l<r && i-q[l].second> len) l++;  f[x][y]=q[l].first + i-q[l].second;  ans=max(ans,f[x][y]);  }}void pts50()//O(n*m*T){int T=0;for (i=1;i<=k;i++){int x,y,fx;scanf("%d%d%d",&x,&y,&fx);T=max(T,y);for (j=x;j<=y;j++) belong[j]=fx;    }//f[t][i][j]表示在第tt段时间到达(i,j)最长的距离memset(f,-127,sizeof(f));/*f[0][x][y]=0;for (t=1;t<=T;t++)  for (i=1;i<=n;i++)     for (j=1;j<=m;j++)      if (st[i][j]!='x')        f[t][i][j]=max(f[t-1][i][j],f[t-1][i-c[belong[t]][0]][j-c[belong[t]][1]]+1);int ans=0;for (i=1;i<=n;i++)  for (j=1;j<=m;j++)      ans=max(ans,f[T][i][j]);printf("%d",ans);*/}int main(){scanf("%d%d%d%d%d",&n,&m,&x,&y,&k);for (i=1;i<=n;i++)  scanf("%s",st[i]+1);memset(f,0x80,sizeof(f));//一个很小的数字 f[x][y]=0;int len;for (i=1;i<=k;i++){int x,y,fx;scanf("%d%d%d",&x,&y,&fx);len=y-x+1;switch(fx){case 1:for (j=1;j<=m;j++) work(n,j,1,len); break;//留出剩下的n行供于遍历case 2:for (j=1;j<=m;j++) work(1,j,2,len); break;case 3:for (j=1;j<=n;j++) work(j,m,3,len); break;case 4:for (j=1;j<=n;j++) work(j,1,4,len); break;}}printf("%d",ans);}