noip2005提高组过河

这是一篇关于动态规划和状态压缩技术在解决NOIP2005提高组过河问题中的应用。文章通过分析题目数据范围,提出利用动态规划的递推公式,并探讨如何进行状态压缩,尤其是处理石子稀疏的情况。作者强调在编程竞赛中注意数据范围的重要性,并给出将石子距离压缩至100的策略,以便进行有效的动态规划求解。
摘要由CSDN通过智能技术生成

介于被这道题折磨了一晚上,因此很有必要记录下来做动态规划及状态压缩中易错事项。

题目大意

       共M个石子,求青蛙每次以S到T步从坐标0跳过坐标L踩到的最小石子数。

样例数据

 输入

10
2 3 5
2 3 5 6 7

输出

2

数据范围

  1 <= L <= 10^9 1 <= S <= T <= 10,1 <= M <= 100

初看此题,可以想到动态规划,递推式为:

         f[i]:=max{f[i-k]}+stone[i];

其中f[i]表示跳到坐标i时踩到到最小石子数目

s<=k<=t

stone[i]为1,则该位置有石子,为0则该位置无石子。


注意到数据范围,L非常巨大,而石子个数却很小,说明石子稀疏(此处提醒我们做题注意数据范围,或在联赛时考虑部分分)。

通过此题学习状态压缩。

通过手算可以发现当中间一段均空时,f[i]值总为一个数。那么这一段可以变短,有很大一部分舍弃不算。所以可以手动改变两石子间距离。

由于T<=10所以最大距离定为100

事实上青蛙可以以T-1和T跳完最小公倍数T(T-1)的距离,这里方便而取T^2,本质一样

当把石子压缩后,就可以像上面一样直接动规了。

那么怎么压缩呢?

这里也是程序实现时要注意的。

如果两石头间距离<100则保持相对位置不变,否则将距离改为100。由于在后面动规程序中只用到stone[i](i是位置坐标),所以这里只用填stone数组即可。

用p计算压缩后在数轴上的坐标,然后赋值为1.


p:=0;
 a[0]:=0;a[m+1]:=l;//假象0处有一块石头,l处有一块石头,方便处理a[1]和l压缩后的位置,这是一个技巧
 for i:=1 to m+1 do
  if a[i]-a[i-1]>100 then
   begin
    stone[p+100]:=1;
    p:=p+100;
   end
  else
   begin
    stone[p+a[i]-a[i-1]]:=1;
    p:=p+a[i]-a[i-1];
   end;
  l:=p;//l是压缩完的长度,也可以看成最后一个石头的压缩后位置(因为最后总要从l向后算t-1个)

注意有不能跳到的点,要适当标记。

program guohe;
var l,s,t,m,ans,i,j,p,tt:longint;
    a:array[0..100]of longint;
    f,stone:array[0..110000]of longint;
begin        
 read(l);
 read(s,t,m);
 for i:=1 to m do read(a[i]);
 if s=t then
  begin
   for i:=1 to m do if a[i] mod t=0 then ans:=ans+1; //S=T直接计算,压缩的话会有问题
   write(ans);
  end
 else
  begin
 for i:=1 to m-1 do
  begin
   p:=i;
   for j:=i+1 to m do
    if a[j]<a[p] then p:=j;
   tt:=a[p];a[p]:=a[i];a[i]:=tt;
  end;
 p:=0;
 a[0]:=0;
 a[m+1]:=l;
 for i:=1 to m+1 do
  if a[i]-a[i-1]>100 then
   begin
    stone[p+100]:=1;
    p:=p+100;
   end
  else
   begin
    stone[p+a[i]-a[i-1]]:=1;
    p:=p+a[i]-a[i-1];
   end;
  l:=p;
 f[0]:=0;
 for i:=1 to l do f[i]:=maxlongint;动规要注意边界!
 for i:=s to t do f[i]:=stone[i];
 for i:=t+1 to l+t-1 do
  begin
   ans:=maxlongint;  //方便处理
   for j:=s to t do
    if ans>f[i-j] then ans:=f[i-j];
   if ans<maxlongint then f[i]:=ans+stone[i];
  end;
 ans:=maxlongint;
 for i:=l to l+t-1 do if ans>f[i] then ans:=f[i];
 write(ans);
  end;
end.


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值