1. 程式人生 > >演算法課7-Divide and Conquer

演算法課7-Divide and Conquer

分治的思想,最經典的兩個例子首先是排序的兩個例子 一個是快排 一個是歸併排序【請看到這裡的你,去複習一下排序演算法】

  1. 快速排序 先總體再區域性
int partition(int nums[],int left,int right){
    int i=left;
    int j=right;
    int temp=nums[i];
    while(i<j){//注意下面內層while的順序
        while(i<j && nums[j]>=temp) {j--;}
        nums[i]=nums[j];
        while
(i<j && nums[i]<=temp) {i++;} nums[j]=nums[i]; } nums[i]=temp; return i; } void quickSort(int nums[],int left,int right){ if(left<right) { int p = partition(nums, left, right); quickSort(nums, left, p - 1); quickSort(nums, p + 1, right)
; } }
  1. 作業例題 【使用歸併排序】 先區域性再總體,需要一個臨時陣列。

F:求逆序對數 總時間限制: 500ms 記憶體限制: 65536kB

描述 對於一個長度為N的整數序列A,滿足i < j 且 Ai > Aj.的數對(i,j)稱為整數序列A的一個逆序 請求出整數序列A的所有逆序對個數

輸入 輸入包含多組測試資料,每組測試資料有兩行 第一行為整數N(1 <= N <= 20000),當輸入0時結束 第二行為N個整數,表示長為N的整數序列 輸出 每組資料對應一行,輸出逆序對的個數 樣例輸入

5 1 2 3 4 5 5 5 4 3 2 1 1 1 0

樣例輸出

0 10 0

#include<iostream>
#include<string>
#include<string.h>
#include<queue>
#include<functional>
#include<vector>
#include <math.h>
#include <algorithm>
using namespace std;

int n;
int num[20005];
int temp[20005];

int res;


void Merge(int low,int mid,int high){
    int i=low;
    int j=mid+1;
    int tmp=low;
    while (i<=mid && j<=high){
        if(num[j]<num[i])
        {
        	//如果aj<ai即左邊的小於右邊的,右邊剩下的都可以構成逆序對
            res+=mid-i+1;            
            temp[tmp++]=num[j++];
        }
        else{
            temp[tmp++]=num[i++];
        }
    }
    while (i<=mid) temp[tmp++]=num[i++];
    while (j<=high) temp[tmp++]=num[j++];

    for(int k=low;k<=high;k++){
        num[k]=temp[k];
    }


}

void mergesort(int low,int high){
    if(low<high){
        int mid=(low+high)/2;
        mergesort(low,mid);
        mergesort(mid+1,high);
        Merge(low,mid,high);
    }

}
int main(){
    while(cin>>n && n){
        for(int i=0;i<n;i++){
            cin>>num[i];
        }
        res=0;
        mergesort(0,n-1);
        cout<<res<<endl;

    }
    return 0;
}

拓展 重要逆序對 總時間限制: 10000ms 單個測試點時間限制: 1000ms 記憶體限制: 65536kB

描述

給定N個數的序列a1,a2,…aN,定義一個數對(ai, aj)為“重要逆序對”的充要條件為 i < j 且 ai > 2aj。求給定序列中“重要逆序對”的個數。

輸入

第一行為序列中數字的個數N(1 ≤ N ≤ 200000)。 第二行為序列a1, a2 … aN(0 ≤a ≤ 10000000),由空格分開。

輸出

輸出一個整數,為給序列中“重要逆序對”的個數。

樣例輸入

10 0 9 8 7 6 5 4 3 2 1

樣例輸出

16

提示 如果使用printf輸出long long型別,請用%lld資料範圍 對於40%的資料,有N ≤ 1000。

同樣的使用歸併排序,在歸併時先用一個指標掃一次,左邊如果有滿足條件的,那麼它的後面的數字都滿足條件。與上題類似,單看merge。 (注意這題的輸出是long long型別)

void Merge(int low,int mid,int high){
    int i=low;
    int j=mid+1;
    int tmp=low;
    int pointer=low;
    while (i<=mid && j<=high){
        if(num[j]<num[i])
        {
            while(pointer<=mid && 2*num[j]>=num[pointer]){
                pointer++;
            }
            res+=mid+1-pointer;
//            res+=mid-i+1;
            temp[tmp++]=num[j++];


        }
        else{
            temp[tmp++]=num[i++];
        }
    }
    while (i<=mid) temp[tmp++]=num[i++];
    while (j<=high) temp[tmp++]=num[j++];

    for(int k=low;k<=high;k++){
        num[k]=temp[k];
    }
}

進階版LeetCode 315. Count of Smaller Number After Self 求每個位置元素對應的逆序對,仍可以用歸併排序。 Java merge sort solution 為了保持原陣列不動,需要維護一個index陣列 注意每次從right中移數,rightcount++;從left中移數,把count陣列加上rightcount 如[2,3,4] [1,2,3] 左邊是2的時候,rightcount=1,把2移入,對應2的count加上1。

  1. 平面最近點對 步驟1:根據點的y值和x值對S中的點排序。 步驟2:找出中線L將S劃分為SL和SR 步驟3:將步驟2遞迴的應用解決SL和SR的最近點對問題,並令d=min(dL,dR)。 步驟4:將L-d ~ L+d內的點以y值排序,對於每一個點(x1,y1)找出y值在y1-d~y1+d內的接下來的7個點,計算距離為d’。如果d’小於d,令d=d’,最後的d值就是答案。 平面最近點對實驗