[CodeVS1748] NOI2005 瑰丽华尔兹

传送门

http://codevs.cn/problem/1748/

题目大意

在一段时间内可以向规定的某一方向移动一格也可以不动,询问最大移动距离

题解

我们用所在的格子位置作为状态,裸的复杂度为 O(N3)
我们发现朝一个方向的转移每次要 O(N) ,然后显然要转移的位置的dp+距离越大是我们想要的,所以单调队列维护 O(1) 的转移
写的好丑…

var
 dp:array[0..205,0..205,0..205]of longint;
 w:array[0..300,0..300]of longint;
 x:array[0..300,1..2]of longint;
 t:array[0..300]of longint;
 i,j,k:longint;
 n,m,s,a,b,c,d,e,l,r,ans:longint;
 cha:char;
function max(a,b:longint):longint;
begin
 if a>b then exit(a) else exit(b);
end;

begin
 readln(n,m,a,b,s); dp[0,a,b]:=1;
 for i:=1 to n do
  begin
   for j:=1 to m do
    begin read(cha); if cha='.' then w[i,j]:=0 else w[i,j]:=1; end;
   readln;
  end;
 for i:=1 to s do
  begin
   readln(c,d,e);
   x[i,1]:=d-c+1; x[i,2]:=e;
  end;
 for i:=1 to s do
  begin
   case x[i,2] of
   1:begin
    for k:=1 to m do
     begin
      l:=1; r:=0;
      for j:=n downto 1 do
       begin
        while (l<=r)and(t[l]-j>x[i,1]) do inc(l);
        if w[j,k]=1 then begin dp[i,j,k]:=0; l:=1; r:=0; end
        else
         if (l<=r)and(dp[i-1,t[l],k]<>0) then dp[i,j,k]:=dp[i-1,t[l],k]+t[l]-j;
        dp[i,j,k]:=max(dp[i,j,k],dp[i-1,j,k]);
        while (l<=r)and(dp[i-1,t[r],k]+t[r]<dp[i-1,j,k]+j) do dec(r);
        if dp[i-1,j,k]=0 then continue;
        inc(r); t[r]:=j;
       end;
     end;
   end;
   2:begin
    for k:=1 to m do
     begin
      l:=1; r:=0;
      for j:=1 to n do
       begin
        while (l<=r)and(j-t[l]>x[i,1]) do inc(l);
        if w[j,k]=1 then begin dp[i,j,k]:=0; l:=1; r:=0; end
        else
         if (l<=r)and(dp[i-1,t[l],k]<>0) then dp[i,j,k]:=dp[i-1,t[l],k]+j-t[l];
        dp[i,j,k]:=max(dp[i,j,k],dp[i-1,j,k]);
        while (l<=r)and(dp[i-1,t[r],k]-t[r]<dp[i-1,j,k]-j) do dec(r);
        if dp[i-1,j,k]=0 then continue;
        inc(r); t[r]:=j;
       end;
     end;
   end;
   3:begin
    for j:=1 to n do
     begin
      l:=1; r:=0;
      for k:=m downto 1 do
       begin
        while (l<=r)and(t[l]-k>x[i,1]) do inc(l);
        if w[j,k]=1 then begin dp[i,j,k]:=0; l:=1; r:=0; end
        else
         if (l<=r)and(dp[i-1,j,t[l]]<>0) then dp[i,j,k]:=dp[i-1,j,t[l]]+t[l]-k;
        dp[i,j,k]:=max(dp[i,j,k],dp[i-1,j,k]);
        while (l<=r)and(dp[i-1,j,t[r]]+t[r]<dp[i-1,j,k]+k) do dec(r);
        if dp[i-1,j,k]<>0 then begin inc(r); t[r]:=k; end;
       end;
     end;
   end;
   4:begin
    for j:=1 to n do
     begin
      l:=1; r:=0;
      for k:=1 to m do
       begin
        while (l<=r)and(k-t[l]>x[i,1]) do inc(l);
        if w[j,k]=1 then begin dp[i,j,k]:=0; l:=1; r:=0; end
        else
         if (l<=r)and(dp[i-1,j,t[l]]<>0) then dp[i,j,k]:=dp[i-1,j,t[l]]+k-t[l];
        dp[i,j,k]:=max(dp[i,j,k],dp[i-1,j,k]);
        while (l<=r)and(dp[i-1,j,t[r]]-t[r]<dp[i-1,j,k]-k) do dec(r);
        if dp[i-1,j,k]=0 then continue;
        inc(r); t[r]:=k;
       end;
     end;
   end;
   end;
  end;
 ans:=0;
 for i:=1 to n do
  for j:=1 to m do
   if dp[s,i,j]>ans
   then ans:=dp[s,i,j];
 writeln(ans-1);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值