分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,简单问题可用二分法完成。
分治算法适用的问题需满足以下性质
1)小规模的问题比大规模的问题更好解决
2)大问题可以分解成若干个小问题,且这些小问题互不干扰
3)最优子结构
4)几个小问题的解可以合并成大问题的解
步骤
1)分解:分解成若干个小问题
2)合并:把几个小问题的解合并成大问题的解
分治的模板
以归并排序为例
时间复杂度O(nlogn)
const int N=1e5+5;
int a[N],tmp[N];
void Mersort(int l,int r)
{
if(l==r) return; //头递归
int mid=(l+r)>>1;
Mersort(l,mid); //分解
Mersort(mid+1,r);
int i=l,j=mid+1;
int cnt=l-1;
while(i<=mid&&j<=r) //合并
{
if(a[i]<a[j])
tmp[++cnt]=a[i],i++;
else tmp[++cnt]=a[j],j++;
}
while(i<=mid) tmp[++cnt]=a[i],i++;
while(j<=r) tmp[++cnt]=a[j],j++;
for(i=l;i<=r;i++)
a[i]=tmp[i];
return;
}
逆序对
同样是O(nlogn)
const int N=1e5+5;
int n;
int a[N],tmp[N];
LL Merge(int l,int r)
{
if(l==r) return 0;
int mid=(l+r)>>1;
LL res=0;
res+=Merge(l,mid);
res+=Merge(mid+1,r);
int i=l,j=mid+1;
LL ago=0;
while(i<=mid&&j<=r) //统计逆序对个数
{
if(a[i]>a[j])
ago++,j++;
else res+=ago,i++;
}
while(i<=mid) res+=ago,i++;
i=l,j=mid+1;
int cnt=l-1;
while(i<=mid&&j<=r) //排序
{
if(a[i]<a[j])
tmp[++cnt]=a[i],i++;
else tmp[++cnt]=a[j],j++;
}
while(i<=mid) tmp[++cnt]=a[i],i++;
while(j<=r) tmp[++cnt]=a[j],j++;
for(i=l;i<=r;i++)
a[i]=tmp[i];
return res;
}