浅析递推(递推专练题解)

在文章的开始我想先说明一下递推是什么,先说教科书上给的定义:
递推算法是一种用若干步可重复的简运算(规律)来描述复杂问题的方法.
听着好像很难懂,要我说,所谓递推就是可以用简单的for循环水过题的一种总称(喂喂……)。
大家也许听过一个典型的递推公式,叫做斐波那契数列。如果你没听过,那就请来观察一下这个数列:0,1,1,2,3,5……
正如你们所发现的这个数列中任连续三项中,前两项的和就等于第三项。如果你想知道在这个数列中第x项的话你可以用下面这个公式。如下
f(x)=f(x−1)+f(x−2) x>2
f(x)=1 0

#include<iostream>
using namespace std;
int main()
{
    int t1=0,t2=1,n,t;
    cin>>n;
    if(n==1)cout<<0<<endl;
    else if(n==2)cout<<1<<endl;
    else
    {
        for(int i=2;i<=n;i++)
        {
            t=t1+t2;
            t1=t2;
            t2=t;
        }
        cout<<t<<endl;
    }
    return 0;
}

看着似乎比递归简单不了多少,但在稍微复杂的题当用递归有点麻烦的时候就是递归发挥作用的时候(求大牛不要用深搜打脸)……
这道题的思路很简单,前面提到过,斐波那契数列的特点就是这个数列中任连续三项中,前两项的和就等于第三项所以就有了上面这个程序。应该不难看懂,接下来开始正式进入今天的正题。
递推专练1
描述 Description
在所有的N位自然数(不包含0)中,有多少个数中有偶数个数字3?
输入格式 Input Format
读入一个数N。
输出格式 Output Format
由于结果可能很大,你只需要输出这个答案mod 12345的值。
注释 Hint
0个3也是偶数个3,并且1位数里不包含0.
1<=N<=1000。
这道题的代码如下。

#include<iostream>
using namespace std;
int n,a[5000],b[5000];
int main()
{
    cin>>n;
    a[1]=8;a[2]=73;
    b[1]=1;b[2]=17;
    for(int i=2;i<=n;i++)
    {
        b[i]=a[i-1]+9*b[i-1];
        a[i]=9*a[i-1]+b[i-1];
        a[i]%=12345;//因为中间值可能会很大很大所以在中间就开始mod,下同。
        b[i]%=12345;
    }
    cout<<(a[n]%12345);
    return 0;
}

通过这个程序可以清晰地看到这道题的递推公式,当然授人以鱼不如授人以渔,这个道理我还是懂的。我们先手动的枚举一位数和二位数中有偶数个3的数,对于一位数来说,偶数个3有0,1,2,4,5,6,7,8,9共9个,二位数中进行简单分析:对于十位数非3的情况,只要个位数非3,那么这个数就有偶数个3,所以对于第一部分析,我们知道二位数中至少有8*9个数字符合要求;再来分析十位数是3时,那么符合要求的数就必须满足个位数也是3,满足要求的只有33这个数字;
再来分析三位数及以上,对于三位数来说,和上述方法一样,只要前两位中有0个3,那么只要百位数非3,那么这个数字就符合要求,则在百位数中,至少有8*9*9个符合要求的数字;再分析百位数是3时,因为百位数是3,所以数字中至少有一个3,则对于前两位来说,如果前两位包含1个3,那么这个数就附和要求,而这前两位包含1个3的规律在哪呢,这里的前两位数字即使分析2位数中十位或个位是3的数字;
递推专练2
描述 Description
  从原点出发,一步只能向右走、向上走或向左走。恰好走N步且不经过已走的点共有多少种走法?
输入格式 Input Format
读入一个数N。
输出格式 Output Format
由于结果可能很大,你只需要输出这个答案mod 12345的值。
注释 Hint
1<=N<=1000。

#include<iostream>
using namespace std;
int main()
{
    long long  shang1=1,zuo1=1,you1=1,shang2=0,zuo2=0,you2=0,n,man;
    cin>>n;
    if(n==1)cout<<3<<endl;
    else
    {
        for(int i=2;i<=n;i++)
        {
            zuo2=(zuo1+shang1)%12345;
            shang2=(zuo1+shang1+you1)%12345;
            you2=(zuo1+shang1)%12345;
            zuo1=zuo2;
            shang1=shang2;
            you1=you2;
        }
    man=zuo1+shang1+you1;
    man=man%12345;
    cout<<man<<endl;
    }
    return 0;
}

公式自己看代码或看下面的题解自己推。
本题的出发点最宜是分别记录三个方向的方法数,因为限定了方向,所以说我们要寻找规律,向上走时,我们可以继续向左右上三个方向移动,而向左移动时,我们可以向上左移动,向右时,可以向上右移动;因此每次向某个方向走的方法数,就等于上一次可以向这个方向走的总方法数,起始值寻找一下规律就能解决;
递推专练3
描述 Description
圆周上有N个点。连接任意多条(可能是0条)不相交的弦(共用端点也算相交)共有多少种方案?
输入格式 Input Format
读入一个数N。1<=N<=1000。
输出格式 Output Format
由于结果可能很大,你只需要输出这个答案mod 12345的值。
时间限制 Time Limitation
1s

#include<iostream>

using namespace std;

int f[1050]={0};
int n;

int main()
{
    cin>>n;
    f[1]=1;
    f[0]=1;
    f[2]=2;
    for(int i=3;i<=n;i++)
    {
        f[i]=f[i-1];
        for(int j=0;j<=i-2;j++)
        {
            f[i]+=f[j]*f[i-2-j];
            f[i]%=12345;
        }
    }
    cout<<f[n]%12345;
    return 0;
}

这道题依旧很水,话不多说,看思路。
本题的突破口就是把图画出来然后注意题中的限定要求,不相交;这道题最关键就是画图,连接了任意两个点以后,圆就会被这个线分割为两个部分,左半部分有n个点假设,右半部分有m个点,n,m的大小均小于输入的点的个数,所以f[n],f[m]的值我们早就在递推式求过了,我们需要知道的是第j个点和不同的点连线后总的方法数,如果j点不进行连接,方法数为f[i-1];连了以后就新增了两个部分,而这两部分的方法数总和即是f[j]*f[i-2-j];

另外好像还有一个递推专练4,可是我做不粗来…………………………
先把题贴这里,希望哪个大神来指导。
描述 Description
在网格中取一个N x 1的矩形,并把它当作一个无向图。这个图有2(N+1)个顶点,有3(N-1)+4条边。这个图有多少个生成树?
样例输入:1
样例输出:4

读入一个数N。1<=N<=1000。

由于结果可能很大,你只需要输出这个答案mod 12345的值。

另,感谢黄山大神的博客,我是跟着山神的思路才A掉这三道题的,所以开始膜拜山神,山神保佑我,加ip,加ip……………………
就这样,题解先写在这里,如果还有水题我会继续为大家推荐的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值