分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解
1、解决算法实现的同时,需要估算算法实现所需时间。分治算法时间是这样确定的:
解决子问题所需的工作总量(由 子问题的个数、解决每个子问题的工作量 决定)
合并所有子问题所需的工作量
2、分治法是把任意大小问题尽可能地等分成两个子问题的递归算法
3、分治的具体过程:
begin {开始}
if ①问题不可分 then ②返回问题解
else begin
③从原问题中划出含一半运算对象的子问题1;
④递归调用分治法过程,求出解1;
⑤从原问题中划出含另一半运算对象的子问题2;
⑥递归调用分治法过程,求出解2;
⑦将解1、解2组合成整修问题的解;
end;
end; {结束}
例题:
有n位选手参加羽毛球赛,比赛要进行n-1天,每位选手都要与其他每一个选手比赛一场并且每位选手每天都要比赛一场,请根据比赛要求
排出选手的比赛日程表。
思路:首先我们拿到问题要给自己打气,哈哈,因为日常的基本问题都跑不出我们所知道算法思想的范畴,此问题也包括在内。
当n是8,16,32时,面对这么一个庞大的问题我们可能就崩溃了,因为我们实在无法求出来,此时我们就要想想是否可以分治一下。
① 就拿16个选手的比赛安排来说,需要比赛15天。
② 分成2个8位选手7天的比赛安排。
③ 分为4个4位选手3天的比赛安排。
④ 分为8个2位选手1天的比赛安排。
相信2位选手1天的比赛安排大家都会吧,如图:
然后退化到第三步即4位选手3天的比赛安排,如图:
在图中可以看出:
第一天:将1,2位和3,4位选手的日程合并。
第二天,第三天:这两天的比赛安排其实可以发现规律的,整个表格可以划分四格,对角赋值。
#include <cstdio>
#include <cstring>
#include <cmath>
#define N 65
int a[N][N]={0};
void gamecal(int k,int n)//处理编号k开始的n个选手的日程
{
int i,j;
if(n==2)
{
a[k][1]=k; //参赛选手编号
a[k][2]=k+1; //对阵选手编号
a[k+1][1]=k+1; //参赛选手编号
a[k+1][2]=k; //对阵选手编号
}
else
{
gamecal(k,n/2);
gamecal(k+n/2,n/2);
for(i=k;i<k+n/2;i++) //填充右上角
{
for(j=n/2+1;j<=n;j++)
{
a[i][j]=a[i+n/2][j-n/2];
}
}
for(i=k+n/2;i<k+n;i++) //填充左下角
{
for(j=n/2+1;j<=n;j++)
{
a[i][j]=a[i-n/2][j-n/2];
}
}
}
}
int main()
{
int m,i,j;
printf("输入参赛选手人数: ");
scanf("%d",&m);
j=2;
for(i=2;i<8;i++)
{
j=j*2;
if(j==m)break;
}
if(i>=8)
{
printf("参赛选手人数必须为2的整数次幂,且不超过64!\n");
return 0;
}
gamecal(1,m);
printf("\n编号 ");
for(i=2;i<=m;i++)
{
printf("%2d天 ",i-1);
}
printf("\n");
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
printf("%4d ",a[i][j]);
}
printf("\n");
}
return 0;
}
运用分治策略解决的问题一般来说具有以下特点:
1、原问题可以分解为多个子问题
这些子问题与原问题相比,只是问题的规模有所降低,其结构和求解方法与原问题相同或相似。
2、原问题在分解过程中,递归地求解子问题
由于递归都必须有一个终止条件,因此,当分解后的子问题规模足够小时,应能够直接求解。
3、在求解并得到各个子问题的解后
应能够采用某种方式、方法合并或构造出原问题的解。
不难发现,在分治策略中,由于子问题与原问题在结构和解法上的相似性,用分治方法解决的问题,大都采用了递归的形式。在各种排序方法中,如二分,三分,归并排序、堆排序、快速排序等,都存在有分治的思想。
/*分治思想的各种算法*/
#include <stdlib.h>
#include <stdio.h>
/*二分检索算法*/
int BinarySearch(int a[],int n,int e)
{
int index;
int low=0,high=n-1, mid;
while(low <= high)
{
mid = (low + high) / 2;
if(e == a[mid])return mid;
else if(e > a[mid])low = mid + 1;
else high = mid - 1;
}
if(low >= high)return -1;
}
/*归并排序算法*/
void MergeSort(int a[],int low,int high)
{
int mid;
int temp[100],i,j,k;
if(high == low)return;
/*先细分排序*/
mid = (low + high)/2;
MergeSort(a,low,mid);
MergeSort(a,mid+1,high);
/*再归并*/
i=low,j=mid+1,k=0;
while(i<=mid && j<=high)
{
if(a[i] < a[j])temp[k++] = a[i++];
else temp[k++] = a[j++];
}
for(;i<=mid;)temp[k++] = a[i++];
for(;j<=high;)temp[k++] = a[j++];
for(i=low,j=0;i<=high;)a[i++] = temp[j++];
}
/*快排,快速划分*/
int Partion(int a[],int low,int high)
{
int v = a[low];
if(low >= high)return;
while(low < high)
{
while(a[high] >= v && high > low)high--;
a[low] = a[high];
while(a[low] <= v && high > low)low++;
a[high] = a[low];
}
a[low] = v;
return low;
}
void QuickSort(int a[],int low,int high)
{
int index;
if(low >= high)return;
index = Partion(a,low,high);
QuickSort(a,low,index-1);
QuickSort(a,index+1,high);
}
/*打印出来*/
void print(int a[],int n)
{
int i = 0;
while(i < n)printf("%d\t",a[i++]);
printf("\n");
}
int main(int argc, char *argv[])
{
//int a[] = {0,1,2,3,4,5,6,7,8,9};
int a[] = {2,4,6,8,9,7,5,3,1,0};
int n = 10;
QuickSort(a,0,9);
print(a,10);
printf("Position : %d\n",BinarySearch(a,n,4));
return 0;
}