分而治之” ( Divide and conquer)方法 (又称“分治术”) ,是有效算法 设计中普遍采用的一种技术。 所谓“分而治之” 就是把一个复杂的算法问题按一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的解,把各部分的解组成整个问题的解,这种朴素的思想来源于人们生活与工作的经验,也完全适合于技术领域。 诸如软件的体系结构设计、模块化设计都是分而治之的具体表现。
分而治之能将问题逐步分解,但并非可用于解决问题的算法,而是一种解决问题的思路。
分而治之算法是递归的,使用分而治之(D&C)解决问题的过程包括两个步骤:
- 找出递归边界条件,这种条件必须尽可能简单
- 不断地将问题分解(或者说缩小规模),直到符合递归边界条件
看着是不是挺简单的,确实,算法这东西难就难在如何将其应用到实际生活中去,而且,算法是学不完的,算法在我看来主要就是一种解题思路,能够帮我们更好的解决生活中的问题,算法可以帮我们更快更好的解决我们遇到的问题,我们学习算法主要是要掌握其中的思想,递归算法有很多很多,但是归根结底就是“分而治之”的思想,当我们掌握了这个思想,我们在面对一系列从未接触过的实际问题的时候,才有机会去利用我们的知识去解决它。
不是所有的分治算法都一定要比蛮力求解算法更高效。但是,通常使用分治算法所用的时间往往比其他方法要少。实际上,分治法孕育了计算机科学中许多最重要和最有效的算法。虽然,许多这类算法的经典例子大部分都为顺序算法,但应该知道,分治法对于并行计算也是非常理想的,因为各个子问题都可以由各自的CPU同时计算。
在分治算法最典型的应用中,问题规模为n的实例往往被划分为两个规模为n/2的实例。更一般的情况下,问题规模为n的实例被划分为b个规模为n/b的实例(其中a个实例需要求解,a>=1,b>=1,a,b为常量)。从理论上分析分治算法运行时间的过程中,往往使用递推公式进行数学分析(离散数学中的递推关系)。为了简化分析,我们假设n是b的幂,对于算法的运行时间T(n),我们有以下递推式: T(n) = aT(n/b) + f(n) (f(n)是一个函数,表示将b个较小规模问题的解合并起来所消耗的时间),该递推公式被称为通用分治递推式。显然,T(n)的增长次数取决于常量a和b以及f(n)的增长。
分而治之算法的案例
Problem:KI的斐波那契
Description
KI十分喜欢美丽而优雅的斐波那契数列,最近他新认识了一种斐波那契字符串,定义如下
f (0) = b, f (1) = a,
f (2) = f (1) + f (0) = ab,
f (3) = f (2) + f (1) = aba,
f (4) = f (3) + f (2) = abaab,
…
KI想知道 f (n) 中的第 m 位是什么,你可以帮他解决这个问题吗?
Input
第一行有一个整数 T ,表示测试组数。
接下来的每个测试组包含两个数 n, m 。
数据范围: T≤ 1000, 0 ≤ n ≤ 90, 1 ≤ m ≤ 1e18
Output
对于每个测试组,输出’a’或者’b’
Sample Input
5
4 1
5 3
10 22
22 233
66 2333333333333
Sample Output
a
a
a
b
a
思路:我们考虑到第n个字符串由第n-1个字符串个第n-2个字符串相加得到,如果我们要找第m个字符,m<=fib[n-1]的话,我们就可以转成求第n-1个字符串的第m个字符,否则,就转换成求第n-2个字符串的第m-fib[a-1]个字符。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll f[100];
void dfs(int n, ll m)
{
if(n == 0) {puts("b"); return;}
if(n == 1) {puts("a"); return;}
if(m <= f[n-1]) dfs(n-1, m);
else dfs(n-2,m-f[n-1]);
}
int main()
{
int t, n; ll m;
f[0] = 1; f[1] = 1;
for(int i = 2; i < 100; i++)
f[i] = f[i-1] + f[i-2];
scanf("%d",&t);
while(t--)
{
scanf("%d%lld",&n,&m);
dfs(n, m);
}
}