歸併分治

yaoguyuan發表於2024-12-06

[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);
}

相關文章