数据结构学习之第五章递归

数据结构学习之第五章递归

0x1 前言

  自己还是太菜了,还有平时上实验课的效率贼低,这次递归学习的4个小实验,第一次折腾了一下午才弄出一个上楼梯的斐波那契数列问题,还好第二次实验写出来了,其中有一个leetcode上的原题恢复ip地址还没做出来,头疼啊,现在老师都讲到了第8章了图的相关知识了,我还停留在第五章,得加把劲才行。

0x2 4个经典递归小实验

0x2.1 问题一: N阶楼梯上楼

  一次可以走两阶或一阶,问有多少种上楼方式?

0x2.1.1 代码

#include <iostream>

using namespace std;

int SolveCount(int n)
{
    if(n<=2)
        return n;
    return(SolveCount(n-1)+SolveCount(n-2));
}
int main()
{
    int n,res=0;
    cout<< "please input n:" <<endl;
    cin>>n;
    res = SolveCount(n);
    cout<< "the result of count: "<< res <<endl;
    return 0;
}

0x2.2 问题二: 求路径和路径条数问题

这里简单描述下问题: 有一个mxn的网格,一个机器人在只能向下和向右走,试求(1,1)->(5,5)的路径数目

0x2.2.1 代码

#include <iostream>

using namespace std;
int count=0;
int m=2,n=5;
void PathCount(int x,int y,int step)
{
    if(y<1 || y>6 || x<1 || x>2)
        return;
    if(x==m && y==n)
        count++;
    //向右走
    PathCount(x+1,y,step+1);
    //向下走
    PathCount(x,y+1,step+1);
}
int main()
{
    int sx=1,sy=1;
    int step=0;
    PathCount(sx,sy,step);
    cout<< "res: " <<count <<endl;
    return 0;
}

0x2.3 问题三: 高效求解\(x^{n}\)

0x2.3.1 问题描述

高效求解\(x^{n}\),要求最多使用O(\(\log(2^{n})\))次递归调用

0x2.3.2 代码

#include <iostream>
#define LL __int64

using namespace std;
int res=1;
int FastMulti(int x,int n,int res,int k)
{
    if(n==0)
        return 1;
    if(n%2!=0)
        res = k*FastMulti(x,n>>1,res,k*k);
    else
        res = FastMulti(x,n>>1,res,k*k);
    return res;
}

int main()
{
    int x,n,k;
    cout<< "please input x:" <<endl;
    cin>>x;
    cout<< "please input n:" <<endl;
    cin>>n;
    k=x;
    res = FastMulti(x,n,res,k);
    cout<< "res: " << res <<endl;
    return 0;
}

0x2.3.3 总结

这个快速幂其实挺有意思的,因为我以前写过一次大概知道是二进制原理,但是很久没写之后就忘记了该怎么写,不过自己琢磨了一番也写了出来,这里可以总结下规律。

首先,\(x^{9}\),9对应的二进制为: 1001 => \(9=1*2^{3}+0*2^{2}+0*2^{1}+1*2^{0}\)

可以发现他们二进制相邻之间就是差了2倍, 所以说我们就可以让k=x一直倍增 \(k^{2}\) \(k^{4}\) 当遇到1的时候我们乘上倍增的结果,为0的时候就继续让k取倍增,这样子我们再把结果相乘指数就是上面的相加。

其实转换的思想就是控制尽量去倍增,比如求$$x^{8}$$的次方 那么直接 \(x^{2}\) \(x^{4} x^{8}\) 3次就好了,但是面对奇数怎么解决呢,我们就可以或者超过的时候怎么解决的问题,我们就可以细化成二进制这个规律去做个转换,真的是很巧妙。

那我们同时也可以去拓宽思路,比如三进制能对应什么?

那么三进制对应的指数倍增是 \(1*3\) \(2*3\) \(0*3\)

那么遇到2的情况就乘多一次k就行了。 比如 12 = 120=> \(x^{6} x^{3} x^{0}\)

这个能进一步优化速度,不过要涉及到位运算。

0x2.4 问题四: 0/1背包问题

0x2.4.1 问题描述

设有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的方案,使选中物品的总重量不超过指定的限制重量,便选中的
物品的价值之和为最大

2.4.2 代码

#include <iostream>

#define N 20

int w[N];
int v[N];
int x[N];

using namespace std;

int SolveRuckSack(int n,int m)
{
    if(n==0 || m==0)
        return 0;
    else if( m>=w[n] && (SolveRuckSack(n-1,m)< SolveRuckSack(n-1,m-w[n])+v[n]))
            {
                x[n]=1;
                return SolveRuckSack(n-1,m-w[n])+v[n];
            }
    else
    {
        x[n]=0;
        return SolveRuckSack(n-1,m);
    }
    //return max(SolveRuckSack(n-1,m),SolveRuckSack(n-1,m-w[n])+v[n]);
}

int main()
{
    int m,n;
    int i=0;
    while(true)
    {
        cout<< "请输入背包的容量(m)与物品的个数(n):";
        cin>>m>>n;
        cout<< "请输入每个物品的重量(w)与物品的价值(v)" <<endl;
        for(i=1;i<=n;i++)
        {
            cout<< "请输入第" << i <<"个背包的信息(重量和价值)"<<endl;
            cin>>w[i]>>v[i];
        }
        cout<< "背包的最优解为:"<< SolveRuckSack(n,m) <<endl;
        cout<< "最优解条件下选择的背包为:" <<endl;
        for(int i=1;i<=n;i++)
        {
            if(x[i])
                cout<< i <<" ";
        }
        cout<<endl;
    }
    return 0;
}

0x2.4.2 总结

这里的背包考虑是以是否选取背包的基础下,利用背包的来做递归的变量,然后通过回退比较,找到最大值 也就是 `max(v[n]+SolveRuckDuck(n-1,m-w[n]),SolveRuckDuck(n-1,m))`

也就是一直在回溯 取了第n个背包的结果然后与n-1去比较,总的来说这里还有有点绕的,因为n和重量都在改变,不过我们可以结合反推,当我们取n的时候如果比之前最大值还可以再大,那么我们还可以再去取的,
这个递归背包变量很重要,很巧妙的想法。

0x3 总结

​ mmp,自己还是太菜了,学习速度太慢了。

posted @ 2019-04-29 17:42  xq17  阅读(318)  评论(0编辑  收藏  举报