分治算法

博客介绍了分治算法,其核心思想是将大问题拆分为小问题,包括“分”“治”“合”三个步骤。还列举了分治算法在循环比赛、轮廓合并、求逆序对、楼层与玻璃瓶强度测试、查找第k小的数等问题中的应用,并分析了各问题的解法及算法复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

我们常常会遇到一些规模很大的问题,大到无从下手,不知道怎么解决。这时候怎么办,将大问题划分为小问题,小问题还不会解怎么办,那就再分,直到问题小到你会解为止。这种思想就是分治。

分治

学习分治,就是三个字,“分”,“治”,“合”。

分(divide):将规模为n的原问题分拆为k个小问题,每个小问题的规模都严格小于n;

治(conquer):如果小问题的规模大于事先设定的阈值n0,则接着分,递归求解,否则直接求解小规模问题

合(merge):将k个子问题的解组合形成原问题的解

算法复杂度

下面来看看分治算法有什么应用

1. 循环比赛

问题:有n=2^k位选手参加一项单循环比赛,即每位选手都要与其他n-1位选手比赛,且在n-1天内每人每天进行一场比赛,1)设计算法以得到赛程安排;2)若比赛结果存放在一矩阵中,针对该矩阵设计O(nlogn)的算法,为各个选手赋予次序P1,P2,…,Pn,使得P1打败P2,P2打败P3,依此类推。

1)
设table[i][j]表示第i个人在第j天遇见的对手

n个选手分两堆

N = 2^k

假设能够对2^k-1大小的堆能够安排比赛顺序table

 对于2^k 大小的矩阵

这样对原table左上角复制到右下角

左下角是左上角矩阵每一个原元素加n/2

右上角矩阵是左下角的复制

新table就是n个人的日程

 

基础情况

2个人的情况可解

T(n) = T(n/2) + O(n ^ 2)

T(n) = O(n^2)

分治

递归

问题解决

伪代码:

Algorithm: GameTable
Input: n //n is the number of game player
Output: t[][]
begin{
    t[][]
    t[][] = Table(n);
    return t[][];
}
end
function Table(int n){
    if(n == 2){
        t[1][0] = 1;
        t[2][0] = 2;
        t[1][1] = 2;
        t[2][1] = 1;
        return;
    }
    half = n / 2;
    Table(half);
    //left down corner matrix
    for(i = half + 1; i <= n; i++){
        for(j = 0; j <= half; j++){
            t[i][j] = t[i - half][j] + half;
        }
    }
    //right down corner matrix 
    for(i = half + 1; i <= n; i++){
        for(j = half + 1; j <= n; j++){
            t[i][j] = t[i - half][j - half];
        }
    }
    //right up corner matrix
    for(i = 0; i <= half; i++){
        for(j = half; j <= n; j++){
            t[i][j] = t[i + half][j - half];
        }
    }
 
}

2)

类似快排

拿出一个选手

赢他的选手都放前面

输的放后面

这样排完序

就是最终结果次序

Algorithm: gameRank
Input:  result[][]
Output: a[i]  //rank
begin{
    a[i] = {1, 2, 3 ... n};
    quickSort(0, n - 1);
}
void quickSort(int a[], int l, int r){
    int i, j, x;
    if(l < r){
        i = l;
        j = r;
        x = a[l];
        while(i < j){
            while(i < j && result[a[j]][x] == 1){
                j = j - 1;
            }
            if(i < j){
                a[i] = a[j];
                i = i + 1;
            }
            while(i < j && result[a[i]][x] == 0){
                i = i + 1;
            }
            if(i < j){
                a[j] = a[i];
                j = j - 1;
            }
        }
        a[i] = x;
        quickSort(a, l, i - 1);
        quickSort(a, i + 1, r);
    }
}

 

2.轮廓合并问题

轮廓合并

已知每一个轮廓的形状,求合并后的总轮廓

分治

将n个轮廓分为两堆

每堆继续求解,继续拆分

直到分为单独的两个

这两个可以合并

类似于归并排序

T(n) = 2T(n/2) + O(m)

//m为房子x轴坐标的范围大小

O(nm)

algorithm Merge_house
struct house{
    int map[m];   // height on x anix
}
Input: house[n]
Output: map;
Begin{
	House = Merge_house(map, 0, n -1 );
	Return house;
}
End

Merge_house(map, p, r)
    if p < r
        q = (p + r) / 2
        house1 = Merge_house(map, p, q)
        house2 = Merge_house(map, q + 1, r)
        houseMerge = MERGE(map, house1, house2);
        return houseMerge;
 
MERGE(map, house1, house2)
    
    for(i = 0; i < m; i++){
        if(house1.map[i] > house2.map[i]){
            map[i] = house1.map[i];
        }
        else{
            map[i] = house2.map[i];
        }
    }

 

3.求逆序对

求任意数组A中逆序对的数目,并证明算法的时间复杂性为Q(nlogn)

方法一

暴力

每一个与后面的所有比对一遍

O(n * n)

方法二

归并

将数列不断拆分

合并a[] and b[]时

如果a[i] > b[j] 则a[i]后面的与b[j]都是逆序对

逆序对数量 + (a.len() - i)

每次还是排序合并的,不然计算过的逆序对在下一次还会再算一遍

T(n) = 2(T(n/2)) + O(n)

O(nlogn)

Algorithm: backPair
Input: a[n]
Output: count
Begin{
    House = Merge_pair(a[], 0, n -1 );
    Return house;
}
End
 
Merge_pair(a[], p, r)
    if p < r
        q = (p + r) / 2
        Merge_house(map, p, q)
        Merge_house(map, q + 1, r)
        MERGE(map, p,q, r);
        return houseMerge;
 
MERGE(a, p, q, r)
    i = p, j = q, k = p;
    temp;
    while(i <= q && j <= r){
        if(a[i] < a[j]){
            temp[k] = a[i];
            k++;
            i++;
        }
        else{
            temp[k] = b[j];
            count = count + q- p - i;
            j++;
            k++;
        }
    }
 

 

4.楼层与玻璃瓶强度

对玻璃瓶做强度测试,设地面高度为0,从0往上有刻度1到n,相邻两个刻度间距离都相等,如果一个玻璃瓶从刻度i落到地面没有破碎,而在高度i+1处落到地面时碎了,则此类玻璃瓶的强度为i。1)若只有一个样品供测试,如何得到该类玻璃瓶的强度;2)如果样品数量充足,如何用尽量少的测试次数得到强度;3)如果有两个样品,如何用尽量少的测试次数得到强度。

1)

只有一个样品

碎了就没了

所以只有从最底层一层一层往上试

顺序查找

O(n)

2)

n个玻璃杯

任性的不行

二分查找

O(logn)

3)

两个

将楼层分组

以根号n个为一组

共根号n个组

拿出一个玻璃杯从小到大测每组的最后一个

测到碎为止

就确定在那个组

然后再那个组里找

O(根号n+根号n)
 

 

5.查找第k小的数

问题:已知序列S= x1,x2,…,xn以及整数k,1≤k≤n,试查找S中第k小的数。

 

方法1

找最小数,找k次

O(kn)

 

方法2

类似于快排

找一个数

能找到它的位置

位置与k比较

知道下一次要找的范围

一次缩小1/2

Algorithm: k-min
Input a[]
Output ans
begin
{
    ans = select(k, 1, n);
}
end
 
function  select(k, left, right){
    x = a[left]
    add = x address of ordered array
    if(k > add){
        select(k, add, right)
    }
    else{
        select(k, left, add);
    }
}
 

总结

分治,分而治之,然后再合并。是不是还有一种帝王君临天下的感觉,其实一样啊,国家那么大,帝王管不过来啊,分呗,分成一个个郡,皇亲将相帮着管,然后每个郡再合并到中央管,也是分治的思想啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值