分治法:
思想:将一个难以直接解决的大问题,划分成一些规模较小的子问题,以便各个击破,分而治之。
求阶过程:(1)划分 (2)求解子问题 (3)合并
启发式规则:
1. 平衡子问题:最好使子问题的规模大致相同。也就是将一个问题划分成大小相等的k个子问题(通常k=2),这种使子问题规模大致相等的做法是出自一种平衡(Balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。
2. 独立子问题:各子问题之间相互独立,这涉及到分治法的效率,如果各子问题不是独立的,则分治法需要重复地解公共的子问题。
这里关于递归算法的介绍在上一部分https://blog.csdn.net/qq_43236424/article/details/105557620
1、最大子段和问题
给定由n个整数组成的序列(a1, a2, …, an),最大子段和问题要求该序列形如
的最大值(1≤i≤j≤n),当序列中所有整数均为负整数时,其最大子段和为0。
例如,序列(-20, 11, -4, 13, -5, -2)的最大子段和为:
分析:
按照平衡子问题的原则将子段平均分成两部分。
首先是要进行划分,分为三种情况:整个子段在左半部分;整个子段在右半部分;最大子段跨左半部分和右半部分。
然后第一种和第二种情况可以直接递归求解,最后一种情况要分别求解左半部分s1和右半部s2分然后最后相加得到最大子段和
最后比较三种情况得到最大子段和的结果取最大值。
参考代码如下:
#include<bits/stdc++.h>
using namespace std;
int Max_three(int a,int b,int c)
{
int max=a;
if(b>max)
max=b;
if(c>max)
max=c;
return max;
}
int Max_Sum(int a[],int left,int right)
{
int sum;
if(left==right)
{
if(a[left]>0) sum=a[left];
else sum=0;
}
else
{
int middle=(left+right)/2;
int Left_Sum=Max_Sum(a,left,middle);
int Right_Sum=Max_Sum(a,middle+1,right);
int mid_left=0;
int mid_right=0;
int tmp=0;
for(int i=middle;i>=left;i--)
{
tmp+=a[i];
if(tmp>mid_left)
mid_left=tmp;
}
tmp=0;
for(int j=middle+1;j<=right;j++)
{
tmp+=a[j];
if(tmp>mid_right)
mid_right=tmp;
}
int mid_sum=mid_left+mid_right;
return Max_three(Left_Sum,Right_Sum,mid_sum);
}
}
int main()
{
int a[100];
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
int tmp=Max_Sum(a,0,n-1);
cout<<"MaxSum:"<<tmp;
}
2、循环赛日程安排问题
设有n=2k个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;
(2)每个选手一天只能赛一次。
按此要求,可将比赛日程表设计成一个 n 行n-1列的二维表,其中,第 i 行第 j 列表示和第 i 个选手在第 j 天比赛的选手。
分析:
按照分治的策略,可将所有参赛的选手分为两部分,n=2k个选手的比赛日程表就可以通过为n/2=2k-1个选手设计的比赛日程表
来决定。递归地执行这种分割,直到只剩下2个选手只要让这2个选手进行比赛就可以了。
这个求解过程是自底向上的迭代过程。在每次迭代中,将问题划分为4部分:左上角、左下角、右上角、右下角。这个算法的关键就是找这4部分元素之间的对应关系。
参考代码如下:
void GameTable(int k, int a[ ][ ]) // n=2k(k≥1)个选手参加比赛,a表示日程安排,下标从1开始
{
n=2; //k=0,2个选手比赛日程可直接求得,即得到左上角元素
a[1][1]=1; a[1][2]=2;
a[2][1]=2; a[2][2]=1;
for (t=1; t<k; t++) //迭代处理
{
temp=n; n=n*2; //填左下角元素
for (i=temp+1; i<=n; i++ )
for (j=1; j<=temp; j++)
a[i][j]=a[i-temp][j]+temp; //左下角元素和左上角元素的对应关系
for (i=1; i<=temp; i++) //填右上角元素
for (j=temp+1; j<=n; j++)
a[i][j]=a[i+temp][(j+temp)% n];
for (i=temp+1; i<=n; i++) //填右下角元素
for (j=temp+1; j<=n; j++)
a[i][j]=a[i-temp][j-temp];
}
}