【算法】几个经典递推关系记录

递推关系数学味很重(貌似很多都是直接有公式的),不过很多记忆搜索或者dp的状态转移都是要自己推理的,记录几个俗爆炸的递推关系...

文科生学计算机......我呵呵一脸 :(

Fibonacci

这个很常见,第一次刷题愣是没找出来...觉得特难(文科生的悲哀)!

目前见过的有 : 兔子生兔子(竟然全是母的还都有孕)母牛生小牛 ; 超级楼梯(一次爬一阶或者两阶) ; 蜜蜂的巢等

总之模型大概是, 现有的 = 已有的+新生的(不一定是f(n-1)和f(n-2))

#include<cstdio>
long long a[50];
int main()
{
    int n;
    a[1]=a[2]=1;
    scanf("%d",&n);
    for(int i=3;i<=n;i++)
        a[i]=a[i-1]+a[i-2];
    for(int i=1;i<=n;i++)
        printf("%lld  ",a[i]);
    return 0;
}

 

 

Catalan数

 

 

最经典的应该是划分凸多边形吧: 固定点n和点1 , 利用k的位置进行分类(2<=k<=n-1),左边是凸k边形 右边是凸n-k+1边形

顺推求解(有点记忆化的味道)

#include<cstdio>
#include<cmath>
using namespace std;
long long a[50];
int main()
{
    int n;
    a[2]=a[3]=1;
    scanf("%d",&n);
    for(int i=4;i<=n;i++)
        for(int k=2;k<=i-1;k++)
            a[i]+=a[i-k+1]*a[k];
    for(int i=3;i<=n;i++)
        printf("%lld  ",a[i]);
    return 0;
}

 

危险的组合

 

这个太有名了...

对于n阶段,从首推到尾部: 假设当前的i是末尾,使得有三个铀连在一起的情况分两种:

1. 前i-1个已经满足条件,此时数量是a[i-1]*2

2. 前i-2和i-1是铀,第i-3个不是,此时数量是pow(2,i-4)-a[i-4];

对于第二种情况是有点恶心的,不过这么想:i-4以前的再怎么摆跟i也没什么卵关系,然后a[i-4]代表i-4做尾部,合法的摆法

 

#include<cstdio>
#include<cmath>
using namespace std;
long long a[50];
int main()
{
    int n;
    a[3]=1;a[4]=3;
    for(int i=4;i<=20;i++)
        a[i]=2*a[i-1]+(pow(2,i-4)-a[i-4]);
    while(scanf("%d",&n)!=-1)
        printf("%lld\n",a[n]);
    return 0;
}

 


全错位排列

 

这个第一次见是装信问题:求n封信全错的装填方案数

 

随便挑出两份A,B,设第i份为A且装错:

1. AB恰好对调,那么这样的B有i-1个

2. A错了,但B不是A的信,那么我们去掉A,设A的信装在B里是对的(因为B已经没对应的信了),恰好划分出一个子问题...(好巧~),这样数量有a[i-2]*(i-1)份(这样的B有i-1个)

最后加法原理...

#include<cstdio>
long long dp[20];
int main(){
    int n;
    dp[2]=1;dp[3]=2;
    for(int i=3;i<=20;i++)
        dp[i]=(dp[i-1]+dp[i-2])*(i-1);
    scanf("%d",&n);
    printf("%lld\n",dp[n]);
    return 0;
}

 

RPG难题

 

话说有n个格子,对这些格子涂色Red Pink Green ,要求相邻颜色不同,首尾颜色不同,求涂色方案数....

笔者第一刷就考虑奇偶去了....(文科生!)

还是从首到尾枚举i为末尾的情况:

1. 若i-1和第一个同色: a[i]=2*a[i-2];(因为对于i-2来说,i-1可以算做第一个,然后i可以涂两个色)

2. 若i-1和第一个异色:第i个只能涂一个颜色,所以a[i]=a[i-1]

最后分类加法....

 

#include<cstdio>
using namespace std;
long long dp[55];
int main(){
    int n;
    dp[1]=3;dp[2]=6;dp[3]=6;
    for(int i=4;i<=50;i++)dp[i]=2*dp[i-2]+dp[i-1];
    while(scanf("%d",&n)!=-1){
        printf("%lld\n",dp[n]);
    }
    return 0;
}

 

RPG的错排

其实前面都水,是基础,不过这个倒是有点意思...

和全错位不同的是这里要求n份信,放错的没超过一半的方案总数;(也就是对的在一半及以上...)

组合数学:乘法原理

枚举装对的从一半到全部: 

设当前装对K封,从n取k有C(n,k)种(文科生注意了,这是组合公式...),剩下的那些全错的可能数有a[n-i]种(a是上面算出来的全错位.....),再乘法原理

最后对枚举的那些用加法原理...(组合数学!文科生!)

 

#include<cstdio>
long long c[55],cnt;
int n;
long long C(int n,int m){
    double ans=1;
    for(int i=0;i<m;i++)
        ans*=(1.0*n-i)/(i+1.0);
    return (long long)ans;
}
int main(){
    int m,i;
    c[2]=1;
    for(int i=3;i<=25;i++)c[i]=(i-1)*(c[i-1]+c[i-2]);
    while(scanf("%d",&n)!=-1&&n){
        m=n/2+n%2;
        cnt=0;
        for(i=m;i<=n;i++)
            cnt+=C(n,i)*c[n-i];
        printf("%lld\n",cnt+1);
    }
    return 0;
}

 

 

 

 

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值