关于递推的两种经典问题

第一种:斐波那契数列:

斐波那契数列的通项公式为:

f[i]=f[i-1]+f[i-2]

这种数列也是很经典的.

代码实现大致如下:

inline void work(){
  f[1]=0;
  f[2]=1;
  for (int i=3;i<=n;i++)
    f[i]=f[i-1]+f[i-2];
}

这种数列还可以推出另一种通项公式的题:

f[i]=f[i-1]+f[i-2]+f[i-3]+...+f[i-m].

我们假设f[1],f[2],...,f[m]都是已知的.

这种题型的暴力形式可以直接这样:

inline void work(){
  for (int i=m+1;i<=n;i++)
    for (int j=2;j<=m;j++)
      f[i]+=f[i-j+1];
}

当然我们可以用前缀和来优化,用F[i]表示f[1]+f[2]+...+f[i].

那么代码如下:

扫描二维码关注公众号,回复: 391016 查看本文章
inline void work(){
  for (int i=1;i<=m;i++)
    F[i]=f[i]+F[i-1];
  for (int i=m+1;i<=n;i++)
    F[i]=2*F[i-1]-F[i-m-1];
}

若要对于mod取模,则可以这样写:

inline void work(){
  for (int i=1;i<=m;i++)
    F[i]=(f[i]+F[i-1])%mod;
  for (int i=m+1;i<=n;i++)
    F[i]=(2*F[i-1]+mod-F[i-m-1])%mod;
}

那么还有一种优化方案:矩阵乘法加速快速幂.

这种优化算法本人已有另一篇博客介绍,这里就不提了.

那么另一种经典递推:卡特兰数列.

令h(0)=1,h(1)=1.

通项公式1:

h(i)=h(0)*h(i-1)+h(1)*h(i-2)+...+h(j)*h(i-j-1)+...+h(i-1)*h(0).

代码实现:

inline void work(){
  f[0]=f[1]=1;
  for (int i=2;i<=n;i++)
    for (int j=0;j<=i-1;j++)
      f[i]+=f[j]*f[i-j-1];
}

通项公式2:

h(i)=h(n-1)*(4*n-1)/(n+1)

代码实现(递归形式):

void h(int n){
  if (n==1) return 1;
  else if (n==0) return 1;
    else return h(n-1)*(4*n-2)/(n+1);
}

非递归形式:

inline void work(){
  f[1]=f[0]=1;
  for (int i=2;i<=n;i++)
    f[i]=f[i-1]*(4*i-2)/(n+1);
}

若要取余,由于除法取余涉及到了乘法逆元,我们一般就直接写通项公式1.

所以卡特兰数的应用主要是组合数学,例如出栈序列统计等.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80262338
今日推荐