算法学习(一)分治算法

1.1 引言

   当我们在处理一些问题时,由于这些问题要处理的数据很多,或者求解的过程很复杂,这时如果直接求解将会在时间上花费很长时间,或者根本没办法求出,对于这一类的问题,我们可以先把它分解为几个子问题,找到并求出这些子问题的相应的解,然后再用适当的方法将他们组合成整个问题的解,这种问题处理的方式,我们就把它叫做分治法。

1.2 算法思路

 (1)分解:将要求解的问题划分成若干规模较小的同类问题;

  (2)求解:当子问题划分的足够小时,用较简单的方法解决;

  (3)合并:按求解问题的要求,将子问题的解进行逐层合并,即可构成最终的解;

1.3 实例:乒乓比赛赛程安排

 设有n=2的k次方个运动员要进行网球循环赛。现要设计一个满足以上要求的比赛日程表:

  (1)每个选手必须与其他n-1个选手各赛一次;

  (2)每个选手一天只能赛一次;

  (3)循环赛一共进行n-1天;

    按照分治策略,可以将所有选手对分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归的用这种一分为二的策略对选手进行分隔,直到剩下两个选手时,比赛日程表的制定就变得简单了,这时只让这两个选手进行比赛就可以了。

    (由于楼主不会发图上传,请各位看官们包涵辣)

    对于8个选手的比赛日程表,其中左上角的与左下角的两小块分别为选手1至选手4和选手5至选手8前三天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,将左下角小块的所有数字按其相对位置抄到右上角,这样就分别安排好了选手从1到选手4和选手5到选手8在后四天的比赛日程!(最重要的就是这个步骤的实现)

    下面附上代码:

    

<span style="font-size:18px;">#include <stdio.h>
#include <conio.h>
#define MaxN 64 //表示最多可安排64位选手来参加比赛
int a[MaxN+1][MaxN+1]={0};
//定义的时候为最大数加一,表示不安排第0号元素参与运算
void gamecal(int k,int n)//处理编号k开始的n个选手的日程
{ /*
1.k表示需要安排的选手的起始序号
2.n表示需要安排的选手的数量 ;
3.k=1,n=4表示从1号选手开始安排四位选手的日程
k=3,n=2表示从3号选手开始安排两位选手的日程
*/
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");
getch();
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");
}
getch();
return 0;
}</span>

     

在这里,因为分治算法还涉及到棋盘覆盖类似于这种的一些问题,楼主有时间会对此进行修改,并将相关问题附录上去,不对的话欢迎拍砖~