首页 > 其他分享 >chapter8-递归与分治

chapter8-递归与分治

时间:2024-03-08 17:13:25浏览次数:26  
标签:chapter8 递归 int 分治 Fibonacci answer include Matrix

1.递归

递归,指直接或间接地调用自身,解决此类题目的方法见我之前走台阶的笔记,重点有2个,(1)分析问题,归纳出递归公式;(2)递归出口。就像俄罗斯套娃一样,要让递归调用总体朝向递归出口推进。

n的阶乘
//递归-n的阶乘 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

long long Factorial(int n) {
    if(0 == n) {
        return 1;
    } else {
        return n * Factorial(n - 1);
    }
}

int main()
{
    int n;
    while(cin >> n) {
        cout << Factorial(n) << endl;
    }
    return 0;
}

汉诺塔问题

用递归策略来处理常规的汉诺塔问题,即把左边的从小到大的圆圈移到右边任意一个空的柱子上,如图所示。设把n个圆圈按照限制挪到最右边的空柱子上,需要的总步数为\(f_n\),那么现在我们要将最大的圆圈挪到最右边这个柱子上,首先要把\(n-1\)个圆圈挪到中间的柱子上,这一步需fn-1步骤数。
汉诺塔1.jpg
再把大圆圈挪到最右边的柱子上,需\(1\)步骤数,由于此时最右边柱子上的圆圈是最大的,所以不存在限制,可以看成是一个空柱子,**此时再把中间的\(n-1\)个圆圈挪到右边柱子上,需fn-1步骤数。

分析之后,得到递归公式:fn = 2 * fn-1 + 1
递归出口:\(f_1 = 1\)

如果考试的时候你能根据递归公式推出通项表达式,进而总结出公式\(f_n = 2^n - 1\),那么直接套公式即可,如果不行,建议还是用递归做比较好。

汉诺塔(Hanoi)
//常规汉诺塔问题 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

long long Hanoi(int n) {
    if(n == 1) {
        return 1;
    } else {
        return 2 * Hanoi(n - 1) + 1;
    }
}

int main()
{
    int n;
    while(cin >> n) {
        cout << Hanoi(n) << endl;
    }
    return 0;
}

HDU OJ 2064 汉诺塔III

汉诺塔III
设把最左边柱子的\(n\)个圆圈,经过中间的空柱子,再放到最右边的空柱子,所需要的总步数为\(f_n\)。那么我们先把\(n-1\)个圆圈放到最右边的柱子上就需要步骤数fn-1;把最大的圆圈挪到中间的柱子上,需要\(1\)步;再把最右边的\(n-1\)个圆圈从最右边挪到最左边,需要fn-1步;此时把最大的圆圈放到最右边,需要\(1\)步。
汉诺塔3.jpg
此时最右边的柱子上只有一个最大的圆圈,不受限制,可以看成是一个空柱子,故此时把最左边的\(n-1\)个圆圈移到最右边,需要步数fn-1

得到递归公式:fn = 3 * fn-1 + 2
递归出口:考虑基础情况,只有一个大圆圈(\(n=1\))在最左边,把它移到最右边需要\(2\)步

汉诺塔III(Hanoi)
//递归-汉诺塔III 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

long long Hanoi3(int n) {
    if(n == 1) {
        return 2;
    } else {
        return 3 * Hanoi3(n - 1) + 2;
    }
}

int main()
{
    int n;
    while(cin >> n) {
        cout << Hanoi3(n) << endl;
    }
    return 0;
}

2.分治

分治策略是另一个非常重要的算法,字面解释就是把一个复杂的问题分成两个或更多个子问题,子问题之间互相独立且与原问题相同或相似。与贪心策略不同,如果子问题仍然不好求解,可以继续再把子问题分成更小的子问题,直到最后的子问题可以简单地直接求解。

由于分治法产生的子问题往往与原问题相同且模式更小,这就为使用递归策略求解提供了条件。在从过程中会导致了递归过程的产生。

分治法的步骤与贪心策略极其相似:
分治法.jpg

求解Fibonacci

Fibonacci数列的求解有三种方法,分别是:
求解Fibonacci.jpg

2.1.1 分治法求解Fibonacci

非常低效,但可以用来理解分治法的精髓,低效的原因是有大量的重复计算,如果我们直接把每一个Fibonacci数列值从小到大保存到一个数组中,那么就是递推法,时间复杂度还不错。

注意,分治法通常会采用分治法进行求解

分治法求Fibonacci
//分治法-Fibonacci 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

int Fibonacci(int n) {
    if(n == 0 || n == 1) {
        return n;
    } else {
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}

int main()
{
    int n;
    while(cin >> n) {
        cout << Fibonacci(n) << endl;
    }
    return 0;
}

2.1.2 递推法求解Fibonacci

递推法求Fibonacci
//递推法-Fibonacci 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 35;
int Fibo[MAXN];

void Initial()
{
    Fibo[0] = 0;
    Fibo[1] = 1;
    for(int i = 2; i < MAXN; ++i) {
        Fibo[i] = Fibo[i-1] + Fibo[i-2];
    }
}

int main()
{
    Initial();
    int n;
    while(cin >> n) {
        cout << Fibo[n] << endl;
    }
    return 0;
}

2.1.2 矩阵快速幂求解Fibonacci

fibo公式.jpg
公式推导:
数学归纳法的方式
fibo1.jpg
fibo2.jpg
fibo3.jpg

矩阵快速幂求Fibonacci
//矩阵快速幂-Fibonacci 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 10;

struct Matrix {
    int matrix[MAXN][MAXN];
    int row, col;
    Matrix() {}
    Matrix(int r, int c): row(r), col(c) {}
};

Matrix Multiply(Matrix x, Matrix y) {
    Matrix answer = Matrix(x.row, y.col);
    for(int i = 0; i < answer.row; ++i) {
        for(int j = 0; j < answer.col; ++j) {
            int sum = 0;
            for(int k = 0; k < x.col; ++k) {
                sum += x.matrix[i][k] * y.matrix[k][j];
            }
            answer.matrix[i][j] = sum;
        }
    }
    return answer;
}

Matrix QuickPower(Matrix x, int n) {
    Matrix answer = Matrix(x.row, x.row);
    for(int i = 0; i < answer.row; ++i) {
        for(int j = 0; j < answer.col; ++j) {
            if(i == j) {
                answer.matrix[i][j] = 1;
            } else {
                answer.matrix[i][j] = 0;
            }
        }
    } 
    //快速幂
    while(n > 0) {
        if(n % 2 == 1) {
            answer = Multiply(answer, x);
        }
        n /= 2;
        x = Multiply(x, x);
    }
    return answer;
}

int main()
{
    int n;
    Matrix start(2,2);
    start.matrix[0][0] = 1;
    start.matrix[0][1] = 1;
    start.matrix[1][0] = 1;
    start.matrix[1][1] = 0;
    Matrix answer(2,2);
    while(cin >> n) {
        answer = QuickPower(start, n);
        cout << answer.matrix[0][1] << endl; //matrix[0][1]和matrix[1][0]一样
    }
    return 0;
}

例题8.4 二叉树

求根结点为m的树中有多少结点数目问题
//分治-二叉树结点m所在子树总共有几个结点 2024-03-08
#include <iostream>
#include <cstdio>

using namespace std;

int NodeNumber(int m, int n) {
    if(m > n) {
        return 0;
    } else { //注意要加m结点本身,拆成子问题,m结点的左右子树+m结点本身
        return 1 + NodeNumber(2 * m, n) + NodeNumber(2 * m + 1, n);
    }
}

int main()
{
    int m, n;
    while(cin >> m >> n) {
        cout << NodeNumber(m, n) << endl;
    }
    return 0;
}

当结点m大于n时,以m为根结点的树为空,该数的结点数目必定为0,这个便是这个问题可被轻松求解的底层问题,也是递归出口

标签:chapter8,递归,int,分治,Fibonacci,answer,include,Matrix
From: https://www.cnblogs.com/paopaotangzu/p/18061405

相关文章

  • 【C++】二叉树的前序、中序、后序遍历(递归、非递归)
    #include<vector>#include<iostream>#include<string>usingnamespacestd;//二叉树的定义structTreeNode{intval;TreeNode*left;TreeNode*right;TreeNode(inta):val(a),left(NULL),right(NULL){}};//使用递归进行前序遍历voidpreoder(Tr......
  • 【C++】翻转二叉树(递归、非递归)
    //使用递归翻转二叉树TreeNode*reverseTree(TreeNode*root){if(!root)returnroot;swap(root->left,root->right);reverseTree(root->left);reverseTree(root->right);returnroot;}//使用队列翻转二叉树层序遍历TreeNode*invertTree(TreeNode*root)......
  • 【洛谷】求第k小的数字(分治算法)
    题目描述如题所述,找到n个数中第K小的数字。但是不同的是时间复杂度要求为O(n),也就是说基本上所有的排序算法都不能用了。这里适合的算法是分治法,也就是使用快速排序。因为这道题的一个特点是只需要得到第k小的数字,而并没有说要对所有元素进行排序。如果我们把所有小于某个元素......
  • mysql根据父节点递归查询所有子节点
    SELECTt3.*FROM(SELECTt1.*,IF(FIND_IN_SET(parent_id,@pids)>0,@pids:=CONCAT(@pids,',',id),'0')ASischildFROM(SELECTt.id,t.parent_id,t.NAMEFROMt_parentAStORDERBYt.idASC)t1,......
  • 使用JMeter的JSON提取器:通过递归下降查找,从接口响应中提取特定字段
    在接口测试中,我们经常需要从返回的JSON数据中提取特定字段以便后续使用。JMeter提供了JSON提取器,可以帮助我们实现这一目标。本文将介绍如何使用JMeter的JSON提取器通过递归下降查找的方式从接口响应中提取特定字段,并通过示例解释JSON表达式中".."的逻辑。1.示例接口响应......
  • Educational Codeforces Round 162 E 点分治 虚树 树形dp
    传送门给出\(n\le2\cdot10^5\)的一棵树,每个节点有一个颜色。求出路径长度为\(2\)路径首端和尾端拥有相同颜色,且路径上其他点不存在相同颜色的点的路径条数。当时看错题了,把颜色抽出来后没法做了。后来感觉能点分治,然后把题看对了,遂写了一个极其抽象的点分治。除此之外,把某......
  • 16 Educational Codeforces Round 142 (Rated for Div. 2)C. Min Max Sort(递归、思维
    C.MinMaxSort很不错的一道题目,不过脑电波和出题人每对上,\(qwq。\)正难则反。我们考虑最后一步是怎么操作的。最后一步一定是对\(1\)和\(n\)进行操作那么上一步呢?上一步应该是对\(2\)和\(n-1\)以此类推第一步应该是对\(\frac{n}{2}\)和\(\frac{n}{2}+1\)我们的答案应该......
  • 点分治学习笔记
    0.前言又称淀粉质。学科营之前赶紧来一波急抓。1.引入我们考虑这样一个问题,对于一棵树,我们求出树上所有路径长度小于等于\(k\)的路径总数。首先不难想到一种\(n^3\)的暴力做法,即枚举两个端点,然后暴力出路径。考虑找路径的时候优化一下,采用倍增或者树链剖分将复杂度变为......
  • 点分治杂题总结
    前言由于已经点明是点分治,所以我们在文章中约定,题解只叙述:求经过当前递归到的\(x\)对于答案的贡献,用以减少文章冗余程度。如有错误,欢迎指出。\(\texttt{1.[BJOI2017]树的难题}\)其实还是比较简单一题,做多了自然就会。首先我们先\(\texttt{dfs}\),在\(x\)的子树上处理一......
  • C语言递归调用子函数
    示例代码1:10进制转16进制查看代码 #include<stdio.h>voiddec2hex(intn){ if(n>15) dec2hex(n/16); if(n%16<10) printf("%c",n%16+'0'); else printf("%c",n%16+55); //printf("%c",n%16<10?n%16+'......