您的位置:首页 > 其它

[BZOJ 1499][NOI 2005]瑰丽华尔兹(DP+单调队列优化)

2015-04-25 11:11 381 查看

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1499

思路

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

f[t][i][j]=max{f[t−1][i][j],f[t−1][ilast][jlast]+1}f[t][i][j]=\max \{ f[t-1][i][j],f[t-1][i_{last}][j_{last}]+1\}

这样做显然太慢,由于钢琴的移动方向是在一段一段的时间内是相同的,因此可以考虑成段DP。令f[t][i][j]f[t][i][j]表示第tt段时间时,钢琴位于(i,j)(i,j)处时,从第11段时间到第tt段时间的最长滑行路程。

f[t][i][j]=max{f[t−1][ilast][jlast]+dist{(ilast,jlast),(i,j)}}f[t][i][j]=\max \{ f[t-1][i_{last}][j_{last}]+dist\{(i_{last},j_{last}),(i,j)\}\}

下面举一个简单例子,当前这段时间内移动方向都是朝右,来讲讲如何利用单调队列优化

可以列出DP方程

f[t][i][j]=max{f[t−1][i][j′]+(j−j′)}=j+max{f[t−1][i][j′]−j′},j−j′≤startt−endt+1f[t][i][j]=\max \{ f[t-1][i][j']+(j-j')\}=j+\max \{ f[t-1][i][j']-j'\},j-j'\leq start_t-end_t+1

第tt段移动方向对应于时间段[startt,endt][start_t,end_t]

jj是个常量,因此我们就是要找到最大的max{f[t−1][i][j′]−j′}\max \{ f[t-1][i][j']-j'\},我们可以维护一个单调队列,保存t−1t-1时刻的所有可行状态的ff值与j′j',保证j′j'单调递增,ff值单调递减,在dp到第tt段时,首先枚举行ii,然后从左到右来枚举jj,并时刻保证队首的j′j'满足限制条件j−j′≤startt−endt+1j-j'\leq start_t-end_t+1,这样的单调队列的队首显然是最优的,用此时的队首的f[t−1][i][j′]f[t-1][i][j']来更新答案。

其他三个方向的DP过程也类似,这样做最终的时间复杂度是O(nmk)O(nmk)

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <queue>

#define MAXN 1000
#define MAXK 300
#define INF 0x3f3f3f3f

using namespace std;

typedef pair<int,int>pr;

int xx[]={-1,1,0,0},yy[]={0,0,-1,1};

int n,m,K,ans,cur;
int sx,sy;
bool mp[MAXN][MAXN];
int f[2][MAXN][MAXN];

struct Segment
{
int st,ed,dir;
}seg[MAXN];

bool operator<(Segment a,Segment b)
{
return a.st<b.st;
}

struct Monoque
{
deque<pr>q;
void clear()
{
q.clear();
}
void Insert(int pos,int val)
{
while(!q.empty()&&q.back().second<val) q.pop_back();
q.push_back(make_pair(pos,val));
}
int query(int pos,int limit)
{
while(!q.empty()&&pos-q.front().first>limit) q.pop_front();
return q.front().second;
}
}monoq;

void update(int time,int cur,int x,int y,int t) //第t段状态区间
{
if(mp[x][y]) //(x,y)是障碍物
{
f[cur][x][y]=-INF;
monoq.clear();
return;
}
monoq.Insert(time,f[cur^1][x][y]-time);
f[cur][x][y]=monoq.query(time,seg[t].ed-seg[t].st+1)+time;
ans=max(ans,f[cur][x][y]);
}

int main()
{
scanf("%d%d%d%d%d",&n,&m,&sx,&sy,&K);
char s[MAXN];
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
mp[i][j]=s[j]=='x';
f[0][i][j]=-INF;
}
}
f[0][sx][sy]=0;
for(int i=1;i<=K;i++)
scanf("%d%d%d",&seg[i].st,&seg[i].ed,&seg[i].dir);
sort(seg+1,seg+K+1);
for(int i=1;i<=K;i++)
{
cur^=1;
if(seg[i].dir==1) //上
for(int y=1;y<=m;y++)
{
monoq.clear();
for(int x=n;x>=1;x--)
update(n-x,cur,x,y,i);
}
else if(seg[i].dir==2) //下
for(int y=1;y<=m;y++)
{
monoq.clear();
for(int x=1;x<=n;x++)
update(x,cur,x,y,i);
}
else if(seg[i].dir==3) //左
for(int x=1;x<=n;x++)
{
monoq.clear();
for(int y=m;y>=1;y--)
update(m-y,cur,x,y,i);
}
else
for(int x=1;x<=n;x++)
{
monoq.clear();
for(int y=1;y<=m;y++)
update(y,cur,x,y,i);
}
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: