归并分治

[Algo] 归并分治

1. 经典归并排序

// 1. 经典归并排序
void merge(vector<int> &v, int left, int mid, int right)
{
    vector<int> tmp(v);
    int i = left, j = mid + 1, k = left;
    while (i <= mid && j <= right) tmp[k++] = v[i] <= v[j] ? v[i++] : v[j++];
    while (i <= mid) tmp[k++] = v[i++];
    while (j <= right) tmp[k++] = v[j++];
    v = tmp;
}
void mergeSort(vector<int> &v, int left, int right)
{
    if (left == right) return;
    int mid = (left + right) / 2;
    mergeSort(v, left, mid);
    mergeSort(v, mid + 1, right);
    merge(v, left, mid, right);
}

2. 归并分治

1)思考一个问题在大范围上的答案,是否等于,左部分的答案 + 右部分的答案 + 跨越左右产生的答案

2)计算“跨越左右产生的答案”时,如果加上左、右各自有序这个设定,会不会获得计算的便利性

3)如果以上两点都成立,那么该问题很可能被归并分治解决(话不说满,因为总有很毒的出题人)

小和问题:

// 2. 小和问题
long crossing(vector<int> &v, int left, int mid, int right)
{
    // 统计
    long ans = 0;
    for (int j = mid + 1, i = left, sum = 0; j <= right; j++)
    {
        while (i <= mid && v[i] <= v[j]) sum += v[i++];
        ans += sum;
    }
    // merge
    vector<int> tmp(v);
    int i = left, j = mid + 1, k = left;
    while (i <= mid && j <= right) tmp[k++] = v[i] <= v[j] ? v[i++] : v[j++];
    while (i <= mid) tmp[k++] = v[i++];
    while (j <= right) tmp[k++] = v[j++];
    v = tmp;
    return ans;
}
long smallSum(vector<int> &v, int left, int right)
{
    if (left == right) return 0;
    int mid = (left + right) / 2;
    return smallSum(v, left, mid) + smallSum(v, mid + 1, right) + crossing(v, left, mid, right);
}
posted @ 2024-12-06 20:21  yaoguyuan  阅读(2)  评论(0编辑  收藏  举报