p1002.pas

来自「www.vijos.cn上一些习题的参考源码」· PAS 代码 · 共 90 行

PAS
90
字号
var
  l,swap,u:dword;
  v:longint;
  s,t,n,m,i,j,sn,re,p,rn:byte;
  stone:array[1..101]of dword;
  dp:array[0..9]of byte;
//------------------------------------------------------------------------------
function min (a,b:byte):byte;
begin
  if a>b then
    exit(b)
  else
    exit(a);
end;
//------------------------------------------------------------------------------
begin
  readln(l);
  readln(s,t,m);
  for i:=1 to m do
    read(stone[i]);
  readln;
  for i:=1 to m-1 do
    for j:=i+1 to m do
      if stone[i]>stone[j] then
      begin
        swap:=stone[i];
        stone[i]:=stone[j];
        stone[j]:=swap;
      end;
  {stone[m+1]:=l;}
  n:=m; while(stone[n]>L) do dec(n);
  stone[n+1]:=l;

  fillchar(dp,sizeof(dp),100);
  dp[0]:=0;
  sn:=1;
  u:=1;
  rn:=0;
  while u<=l do
  begin
    i:=u mod t;
    p:=100;
    for v:=u-s downto u-t do
    begin
      j:=(v+t) mod t;
      if u<stone[sn] then
        p:=min(p,dp[j])
      else
        p:=min(p,dp[j]+1);
    end;

    if u=stone[sn] then inc(sn);

    if dp[i]=p then
      inc(rn)
    else
    begin
      rn:=0;
      dp[i]:=p;
    end;
    inc(u);
    if (rn>=t) and (u<stone[sn]) then
    begin
      {u:=(stone[sn]-u) div t*t+u;}
      u:=stone[sn]-t;
      rn:=0;
    end;
  end;
  re:=100;
  {dec(dp[l mod t]);}
  for i:=0 to t-1 do
    re:=min(re,dp[i]);
  writeln(re);

end.


{
动态规划
这道题的方程很好想,设dp[i]表示到i点最少碰到的石子数
dp[i]=dp[i-k]+s[i](s<=k<=t),i处是石头则s[i]=1,否则=0。
但题目的规模是109,我们要解决两个问题,一是空间问题,二是时间问题。

空间问题:我们发现k的值最大只有10,所以可以用滚动数组,问题解决。
时间问题:朴素的做法是O(lt)的肯定要超时,但我们发现l最大109,m最大只有100,也就是说,在大多数情况下,两个石子的距离是相当大的,而动态规划却在这个相当大的距离上耗费太多的时间,算法的瓶颈就在这里。
不知道大家有没有看过星战,两个距离非常大的地方可以通过超时空来瞬间到达,这是个很好的思路。
顺着这个思路,我们设想一下,在这个距离内没有石子,意味着dp不会增加,只能不断调用之前的值。这时如果走了一圈发现没有任何变化,那么再走一圈呢?肯定依然没有变化,所以这时就可以"超时空"到下一个石头附近。
我们设rn记录重复次数,如果这个次数>=t,就是循环了一圈,这时直接到下一个石头附近,算法的复杂度是O(mt),问题解决。
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?