http://www.lydsy.com/JudgeOnline/problem.php?id=1499
一个很显然的 O(nmT) 的做法就是令 f[t][i][j] 表示时刻 t 时,钢琴位于 (i,j) 处时,从时刻 1 到 t 的最长滑行路程。很容易得到DP方程
j 是个常量,因此我们就是要找到最大的 max{f[t−1][i][j′]−j′} ,我们可以维护一个单调队列,保存 t−1 时刻的所有可行状态的 f 值与 j′ ,保证 j′ 单调递增, f 值单调递减,在dp到第 t 段时,首先枚举行 i ,然后从左到右来枚举 j ,并时刻保证队首的 j′ 满足限制条件 j−j′≤startt−endt+1 ,这样的单调队列的队首显然是最优的,用此时的队首的 f[t−1][i][j′] 来更新答案。
其他三个方向的DP过程也类似,这样做最终的时间复杂度是 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;
}