核电站问题 解题报告

核电站问题(SGOI)

Time Limit:10000MS  Memory Limit:65536K

Description
  一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。
  任务:对于给定的N和M,求不发生爆炸的放置核物质的方案总数。

Input

  输入文件只一行,两个正整数N,M( 1 < N < 50,2 ≤ M ≤ 5 )

Output

  输出文件只有一个正整数S,表示方案总数。

Sample Input

4 3

Sample Output

13

Source

SGOI


原文 http://www.mydrs.org/program/list-id=166.htm

【算法分析】

  本题是典型的组合计数问题,且数据量较大,所以应使用组合数学的算法实现。
  运用升格思想,设N个坑不会发生爆炸的组合数为F(n)。假设已知F(n)以前的放置方案,在n-1个坑的左边再加一个坑得n个坑。则第n个坑的放置方案有以下情况:(为便于叙述,我们把放入核物质称为1,未放入称为0,则n个坑的状态表示为二进制数)

  1、当该坑右边已有连续m-1个核物质时(即形如111…1110*),则该坑只能不放核物质,否则将发生爆炸。因为前m个状态已确定,此种情况总数为F(n-1-m)
  2、对于剩下的情况,新加入的第n个坑可以为1,也可为0。此种情况总数为2*[F(n-1)-F(n-1-m)]。
通过加法原理,易得F(n)的递推关系式:
  f(n)=2*f(n-1)-f(n-1-m)
  f(-5)=f(-4)=f(-3)=f(-2)=0, f(-1)=f(0)=1

【程序实现的技巧】

  递推关系式建立了,编程实现就没什么困难了,但还应注一下问题的规模。本题N的规模可达50,根据不等式F(50)<=250≈1015,已超过长整型的范围。但如果使用高精度计算势必会加大编程复杂度。事实上1015并不是非常大,选用合适的数据结构完全可以避免高精度运算。TurboPascal支持一种称为Extended的实型数组,它可支持最多20位的有效数字。完全可以适应本题的要求。如此一来,程序就只有短短的四行了,源程序见附录。
【进一步的优化】

  首先是空间上,以上算法虽然可行,但不难发现当前状态的值只与前面有限的几项有关系,如果我们将其全部储存必然会造成空间上巨大的浪费。一种常见的做法是在数组中只保存前N项关联的数据,算完一个阶段后再刷新,将所有数据向上一阶段复制,遗弃最前面的那个阶段,继续算下一阶段。这种方法虽然可行,但数据的复制要耗费额外的时间,并且当相关的阶段较多时对原来程序要做的改动较大,既不方便又易出错。下面介绍一个本人自己在编程时摸索出来的方法,实现起来非常简单,同时也省去了复制数据的时间。设当前阶段与前M阶段相关,建立数组F[0..M],数组存储内容与原来一样,不过访问数组下标I时,使用F[I Mod(N+1)]代替。这样只需作很小的改动,就可获得大量空间,并且Pascal处理Mod的速度是很快的,这种方法几乎没有额外的时间消耗。

  在时间效率上,本算法也有可优化的地方。题目只要我们求出N规模的解,而我们通过递推关系式,把1-N规模的解都求出来了,这无疑是一个浪费。尤其是当问题规模进一步增大时,往往考虑使用母函数将问题的递推关系转化为关于N的函数直接求解。限于篇幅这里就不介绍了。
  总之,程序=算法+数据结构,程序的优化是无止境的,我们在平常的编程中要注重这方面的训练,才能从中获益。

附上自己的代码:

#include
using namespace std;
long long f[60];
int main()
{
     int i,n,m;
     scanf("%d%d",&n,&m);
     f[4]=f[5]=1;
     for (i=6;i

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值