1. 程式人生 > 遊戲攻略 >《天命奇御2》杭州支線任務達成指南

《天命奇御2》杭州支線任務達成指南

前言

聽了yxcacwing開的基礎演算法課的其中一節,聽了雙指標演算法,感覺事實上它還是暴力的做法,但是一分析,它居然是一個O(n)的演算法,不禁有點好奇,剛開始是有一點搞不懂,總感覺它還是有兩層迴圈,但是一分析,發現它所有的元素頂多遍歷了一遍,兩個指標最多也就是走了2n次。感覺有點奇妙,因此寫了篇筆記。

雙指標演算法

我們其實已經接觸過雙指標演算法了,只是我們自己並不知道而已,在哪裡接觸過呢?沒錯,再寫歸併排序的程式碼的時候以及快速排序的程式碼的時候。這裡,我引用一段歸併排序的合併操作 的程式碼作為例子。

/* 合併操作 */
void merge(int arr[],int l,int mid ,int r){
    int p1=l,p2=mid+1;
    int help[r-l+1],i=0;
    while(p1<=mid && p2<=r){
        help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
    }
    /* 將沒導完的資料導到help */
    while(p1<=mid){
        help[i++]=arr[p1++];
    }
    while(p2<=r){
        help[i++]=arr[p2++];
    }
    /* 將help陣列拷貝回給arr,注意,合併的是l到r這個區間,並不是0-盡頭 */
    for(int k=0;k<i;k++){
        arr[l+k]=help[k];
    }
}

歸併排序我們的做法是什麼?是不是將陣列分成兩段,然後分別對這兩段進行排序,然後將這兩段整合到一個數組中,這個整合的過程就用到了雙指標演算法,我們看上面的程式碼,我們兩個指標p1p2分別指向排好序的兩段,然後我們判斷arr[p1]arr[p2]的值的大小,如果說,arr[p1]小的話,那就把arr[p1]填到中間陣列,然後p1就往後移,同樣的p2也是如此。也就是說,我們用兩個指標就可以把兩段排好序的陣列有序地排到第三個陣列當中來,原理就是,誰小誰就裝進去,一直裝到完為止。這樣,我們會發現,對於左半段的陣列上的每一個值,p1都只訪問了一遍,同樣對於右邊的陣列,p2也只是訪問了一遍。也就是說,將兩個排好序的陣列(設為陣列1

陣列2)整合為一個數組(也要求排好序),假設陣列1的長度為a,陣列2的長度為b,那麼整合操作索要進行的運算元最對為a+b。如果我們用固定某個陣列的一個值,然後遍歷另一個數組的所有值得做法得話,複雜度是O(a*b),可見,用雙指標演算法可以達到優化效果。

雙指標演算法寫法

本來我以為雙指標演算法只有一種寫法,那就是y老師教的那種,但是我看了看歸併排序那一題,好像那也是一種寫法,而且好像更容易理解。這裡把兩個模板都給出來吧

這是y老師給的模板:

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < b && check(i, j)) j ++ ;

    // 具體問題的邏輯(題目要求幹嘛)
}
常見問題分類:
    (1) 對於一個序列,用兩個指標維護一段區間
    (2) 對於兩個序列,維護某種次序,比如歸併排序中合併兩個有序序列的操作

這裡解釋一下上面程式碼的大概意思,就是說,ij都是從0開始,然後如果j所指的東西滿足某種性質的話或者他沒有到達邊界,那j++,用上面歸併排序來說明一下:如果arr[p2]<arr[p1],那麼我把arr[p2]存入help,然後p2++,如果下一個還滿足的話,同樣執行這樣的操作,一直到arr[p2]>=arr[p1]的時候才停止,這時,執行上面操作的就變成p1了,然後知道陣列全部判斷完畢。這裡我改寫一下上面歸併排序的程式碼,以方便理解:

/* 合併操作 */
void merge(int arr[], int l, int mid, int r) {
    int help[r - l + 1], i = 0;
    for (int p1 = l, p2 = mid + 1; p1 <= mid; p1++) {
        while (arr[p2] < arr[p1] && p2 <= r) help[i++] = arr[p2++];
        help[i++] = arr[p1];
    }
    /* 將help陣列拷貝回給arr,注意,合併的是l到r這個區間,並不是0-盡頭 */
    for (int k = 0; k < i; k++) {
        arr[l + k] = help[k];
    }
}

這是另一種形式:

while(i<=a&&j<=b){
    ······
}
while(i<=a){//處理i沒走完的
    ······
}
while(j<=b){
    ······
}

個人感覺這一種比較好理解

以CCF-CSP,202006-2稀疏向量為例

在這篇部落格中我用了上面兩種解法作為例子。大家可以去那邊看一看。

結束

好了,到這裡算是已經寫完啦。