分治法的核心思想:
①大问题能分解为子问题,子问题之间相互独立,并且与原问题相同。
②分别求解子问题,如果子问题的规模仍然不够小,则再次划分为k个子问题,如此递归的进行下去,直到问题规模足够小,直到容易求出其解为止。
③合并解,自底向上逐步求出原来问题的解,合并所有的解。合并的代价因情况不同有很大的差异,分治的有效性很大程度上依赖于合并的实现。
分治法的适用场合:
运用分治策略解决的问题一般来说具有以下特点:
①若问题的规模缩小到一定的程度可以容易地解决
②若问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
③若用该问题分解出的子问题的解可以合并为该问题的解
④若问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
第①条特征是绝大多数问题都是满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第②条特征也是大多数问题可以满足的,此特征反映了递归思想的应用;
第③条特征很关键,能否利用分治法完全取决于问题是否具有第三条特征,如果只具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第④条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
分治算法的典型例题
问题描述:在实际问题中,通常会遇到许多第二的问题。比如:第二名,第二大和第二小等。在已知的n个数据中找出其中第二小的数据。
确定算法:根据问题描述,利用数组a[n]存储已知数据,定义两个变量,b和c,分别用来存储数据中的第一小值和第二小值,再对已知数字进行一趟比较之后,便可从中选出第二小的数据,输出即可。此算法的时间复杂度为O(n).
设计算法:用二分法解决本问题的一般思想:将数组平均分为两个子集,如果只选取第二小数据或是只选取最小的数据,合并处理后都有可能得不到原问题的解。但是,若在两个子集中都选取最小的两个数据,那么原问题第二小的数据则一定在这4个数中。这样就将问题转化为“求一组数中较小的两个数”,二分法分解后就将原问题分解为“与原问题独立且相似的两问题”。将两问题的解合并,从而得到原问题的解,即可求出一组数中第二小的数据。
#include<iostream>
#include<algorithm>
using namespace std;
int second2(int a[], int n) //求第二小的函数
{
int i, b, c;
if (a[0] < a[1]) //如果a[0]小于a[1]
{
b = a[0]; //把a[0]赋给最小值
c = a[1]; //a[1]赋给第二小值
}
else {
b = a[1];
c = a[0];
}
for (i = 2; i < n; i++) //i从第二个数开始遍历
if (a[i] < c) //若某个数小于c
{
if (a[i] < b) //并且这个数也小于b
{
c = b;
b = a[i]; //把该数的值赋给最小值
}
else
c = a[i]; //则把它的值赋给第二小值
}
return c; //将第二小的值返回
}
int main() {
int a[1000], i, m;
cout<<"请输入数据个数m:";
cin>>m;
cout<<"请输入m个已知数据:";
for (i = 0; i < m; i++)
cin>>a[i];
cout<<"第二小的数据为%d"<<second2(a, m);
}