又回到我可爱的Blog了~昨天偷了点懒,一篇文章也没写~今天一定抽时间补上。
下面切入正题。
【题目描述】(rqnoj105) 一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。 任务:对于给定的N和M,求不发生爆炸的放置核物质的方案总数 【输入格式】 输入文件只一行,两个正整数N,M( 1<N<50,2≤M≤5) 【输出格式】 输出文件只有一个正整数S,表示方案总数。 【样例输入】 4 3 【样例输出】 13
这是一道线性递推类的动态规划。说实话,这道题我直接看的题解,竟然没有看懂,纠结我一节课…先把代码贴上:
1 program nuclear;
2 var
3 f: array [ - 6 .. 60 ] of int64;
4 i,m,n:integer;
5 begin
6 readln(n,m);
7 f[ 0 ]: = 1 ;
8 f[ - 1 ]: = 1 ;
9 for i: = 1 to n do
10 f[i]: = 2 * f[i - 1 ] - f[i - m - 1 ];
11 writeln(f[n]);
12 end .
这巨短的代码理解起来还真不容易。首先,f数组存储的是到第f个坑可能出现的情况数。网上很多题解都用到分情况讨论,根据Jingo大牛的无敌思想,只要把数组向前多定m位,即-m到n即可。
另外一个比较纠结的地方就是这个神奇的动态方程。你可以自己分析一下,如果不懂请看下文。
我们先将方程分解,即f[i]:=f[i-1]+f[i-1]-f[i-m-1],这是显而易见的。两个f[i-1]分别代表的是f[i]位上放或者不放。但是f[i]上能放的条件是连续的一排上最多有m-1堆。以样例为例,当i=4时有以下状态:
因为如果在(4)放,在(3)号坑放的前提下(2)一定是不放的。所以要用在f[i-1](3号坑放的那一个)中排除有(2)且有(3)的那种情况。
由题意可知,如果(2)(3)都有核物质,(1)中一定没有。所以能达到图中的的状态的情况数只等于f[0],即f[i-m-1]的值。这时f[i-1]-f[i-m-1]就好理解了。
还有一个问题就是为什么f[0]和f[-1]都初始化为1。这可以从上文很简单的找到答案。因为当i=m(i-m-1=-1)或i=m+1(i-m-1=0)时,需要减去i,i-1,…,i-m+1都有核物质的这一种不符合题意的情况。
现在请把鼠标滚轮向上滚,重新看一遍代码。这是你就可以体会到这段伟大的O(n)的DP代码的魅力了。
(saltless原创,转载请注明出处)