【原题】【noip 2005 T2】【动态规划】过河(线性动态规划)
问题
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点: 0 , 1 ,……, L (其中 L 是桥的长度)。坐标为 0 的点表示桥的起点,坐标为 L 的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是 S 到 T 之间的任意正整数(包括 S,T )。当青蛙跳到或跳过坐标为 L 的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度 L ,青蛙跳跃的距离范围 S,T ,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入格式 Input Format
输入文件的第一行有一个正整数 L ( 1 <= L <= 10^9 ),表示独木桥的长度。第二行有三个正整数 S , T , M ,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中 1 <= S <= T <= 10 , 1 <= M <= 100 。第三行有 M 个不同的正整数分别表示这 M 个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出格式 Output Format
输出文件只包括一个整数,表示青蛙过河最少需要踩到的石子数。
分析
方程的话,相信所有人都能写出f[i]表示跳到i位置时,踩到的最少的石子数,那么
f[i]=min(f[i-j])+1(i位置是石头)
f[i]=min(f[i,j])(i位置不是石头),其中j枚举当前这一步跳的距离。
根据题目的数据范围,显然这样的算法是过不了的。方程好像也无法再优化,那么我们发现这道题中,桥巨长,石头巨少,所以我们考虑---状态压缩,即如果两块石头间的距离大于100我们就记为100。这样是不影响结果的,至于怎么证明,略……………………
还有几个地方特别的需要我们注意
1、如果青蛙跳的距离是一个定值,那么我们可以特殊处理。
2、题目并未保证石子是有序给出的,所以,为了保险起见,我们要对石子进行升序排列。
反思
方程绝对不等于得分,不但细节要注意,可能的情况,也要考虑到。noip的题绝对不可能白白送分,有的要求,在题目描述里并未出现。但是,会直接影响到得分。这就要求我们多想,多思考,不要急于打代码,打一道题就要做到可提交的程度。
code
var a:array[0..100000] of 0..1; f:array[0..100000] of longint; stone,st:array[0..100] of longint; l,s,t,m,i,j,ans:longint; function min(a,b:longint):longint; begin if a<b then exit(a); exit(b); end; begin readln(l); readln(s,t,m); for i:=1 to m do read(stone[i]); if s=t then begin for i:=1 to m do if stone[i] mod t=0 then inc(ans); writeln(ans); halt; end; for i:=1 to m-1 do for j:=i+1 to m do if stone[i]>stone[j] then begin stone[0]:=stone[i]; stone[i]:=stone[j]; stone[j]:=stone[0]; end; stone[0]:=0; stone[m+1]:=l; l:=0; for i:=1 to m+1 do if stone[i]-stone[i-1]>=100 then begin st[i]:=st[i-1]+100; a[st[i]]:=1; l:=l+100; end else begin st[i]:=st[i-1]+stone[i]-stone[i-1]; a[st[i]]:=1; l:=l+stone[i]-stone[i-1]; end; a[0]:=0; a[st[m+1]]:=0; filldword(f,sizeof(f)>>2,maxlongint>>1); f[0]:=0; for i:=1 to l+t do for j:=t downto s do if i-j>=0 then f[i]:=min(f[i],f[i-j]+a[i]); ans:=maxlongint; for i:=l to l+t do ans:=min(ans,f[i]); writeln(ans); end.