一:算法设计思想
1.1 分治法求解问题的过程
- 将整个问题分解为若干个小问题后分而治之,如果分解的子问题还能够继续分解,则反复使用分治策略将问题继续划分,直到无法再继续分解为止,必要的时候合并这些小问题的解,就能够获得大问题的解。
- 算法的特征可以理解为递归的过程,其中可以分为三个步骤
- 分解:将原问题划分为若干个规模较小的,相互独立,与原问题形式相同的子问题。
- 解决:若子问题规模较小且比较容易解决,则直接求解,否则则继续划分为较小的部分。
- 合并:将已经求解的各个子问题的解逐步合并为原问题的解。
1.2 适用范围
- 求解问题的规模为n且取值相当大的问题,使用蛮力策略效率得不到保证,如果满足下面的条件,就能够使用分治法求解
- 能将这n个数据划分为k个不同的子集,并且k个子集又能够独立求解的子问题,其中1<k<=n
- 分治法得到的子问题与原问题有相似的结构,便于利用递归和循环机制
- 在求解出这些子问题的解后,就能够推出原问题的解
二:实际问题
2.1 金块问题
2.1.1 问题描述
- 老板有一袋金块(共n块,n是2的幂(n≥2)),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块。假设有一台比较重量的仪器,希望用最少的比较次数找出最重和最轻的金块。参考链接
2.1.2 蛮力法
2.1.2.1 算法设计
- 对金块进行逐个查找,先拿到两块中比较重的那一个,然后留下来与下一块比较,直到全部比较完成,就找到,算法类似于一趟选择排序
2.1.2.2 代码
def minmax(a, n):
max = a[0]
min = a[0]
for i in range(2, n):
if max<a[i]:
max = a[i]
if min > a[i]:
min = a[i]
return max, min
2.1.3 分治法
- 将数据分为两个组,目的是选择其中的最小值和最大值
- 递归分解知道每个元素的个数<=2可简单的找到最大的值和最小的值
- 回溯时合并子问题的解,在两个子问题的解中大者取大,小者取小,合并为当前问题的解
- 代码
float min(float x,float y)
{
if(x<y)
return x;
else
return y;
}
float max(float x,float y)
{
if(x>y)
return x;
else
return y;
}
float Find_min(float A[],int left,int right)
{
float la,ma,ra;
if(left==right)
{
float min;
min=A[right];
return min;
}
if(right-left==1.0)
{
la=A[left];
ra=A[right];
return(min(la,ra));
}
if(right-left>1.0)
{
ma=(left+right)/2.0;
la=Find_min(A,left,ma);
ra=Find_min(A,ma,right);
return(min(la,ra));
}
}
float Find_max(float A[],int left,int right)
{
float la,ma,ra;
if(left==right)
{
float max;
max=A[right];
return max;
}
if(right-left==1.0)
{
la=A[left];
ra=A[right];
return(max(la,ra));
}
if(right-left>1.0)
{
ma=(left+right)/2.0;
la=Find_max(A,left,ma);
ra=Find_max(A,ma,right);
return(max(la,ra));
}
}
int main()
{
float A[100];
int n;
float min;
float max;
printf("请输入金块数目:\n");
scanf("%d",&n);
printf("请输入%d块金子的重量:\n",n);
for(int i=0;i<n;i++)
scanf("%f",&A[i]);
printf("最重的金块:");
max=Find_max(A,0,n-1);
printf("%.1f\t\t",max);
printf("最轻的金块:");
min=Find_min(A,0,n-1);
printf("%.1f",min);
printf("\n");
return 0;
}