Codility上的練習(12),codility上練習12

來源:互聯網
上載者:User

Codility上的練習(12),codility上練習12

(1) MinMaxDivision

給定一個非負整數數組,每個整數都是[0..M]之間的,你要把它分成K段,(切K - 1刀),段可以為空白,每個元素必須屬於一段,每段必須包含0個或者多個連續的元素,要求分好和最大段的和盡量小,返回這個儘可能小的最大和。

資料範圍 :N, K [1..10^5], M [0..10^4]

要求複雜度 時間 O(N * log(N + M)) 空間 O(1)。


分析:典型的二分我們可以。二分一個最大段的和,然後我們一段一段地加,超過要加的值,就開始一段新的。這個方法得力雩都是非負整數……

簡單說一下複雜度,二分問題的架構就是 二分 + 判斷。 判斷部分顯然是O(N)的。 二分的複雜度取決於二分的區間大小。我們的二分區間左端點可以認為是min(A[i]),也可以認為是0,反正區間大點也關係,右端點最大是N * M,那麼二分的複雜度是O(log(N * M)) = O(logN + logM) = O(2 * log(max(M, N)) = O(log(max(M, N)) = O(log (M + N)) 所以算上檢測的複雜度就達到要求的那個了。

// you can use includes, for example:// #include <algorithm>// you can write to stdout for debugging purposes, e.g.// cout << "this is a debug message" << endl;bool can(vector<int> &a,int x, int k) {int sum = 0;    --k;    for (int i = 0; i < a.size();) {        if ((sum += a[i]) > x) {            if (--k < 0) {                return false;            }            sum = 0;        }        else {            ++i;        }            }    return true;}int solution(int K, int M, vector<int> &A) {    // write your code in C++98    int left = 0,right = -1;    for (int i = 0; i < A.size(); ++i) {        right += A[i];        left = max(left, A[i]);            }        while (left <= right) {        int mid = (left + right) >> 1;        if (can(A, mid, K)) {            right = mid - 1;        }        else {            left = mid + 1;        }    }    return right + 1;    }

(2) NailingPlanks

N塊木板,可以看作N線段,給定兩個長度為N的正整數數組A[],B[],[A[k],B[k]]表示木板(線段)的起點和終點,A[k] <= B[k]。有M個釘子,它們分別在長度為M的正整數數組裡。釘子I可以固定住木板K,當且今當A[K]<=C[I]<=B[K]。問按順序使用釘子,至少使用前多少個釘子可以固定住所有木板?無解返回-1。

資料範圍: 木板數N和M的範圍[1..30000], A B C數組元素範圍為[1..2 * M]

要求複雜度: 時間O((N+M)*log(M)) , 空間O(M)

分析: 一個顯然的並且符合要求的演算法是二分答案,問題是如何判斷,顯然我們不能迴圈木板和釘子。但是我們可以計算從開頭到當前位置一共有多少個釘子,這是首碼和的思想。計算首碼和需要O(M),判斷需要O(N),二分是O(logM),所以整好是要求的時間複雜度。空間上需要存首碼和O(M)。

代碼:

// you can also use includes, for example:// #include <algorithm>int solution(vector<int> &A, vector<int> &B, vector<int> &C) {    // write your code in C++98    int m = C.size();    int M = (m << 1) | 1;    int left = 0, right = m, result = -1;    while (left <= right) {        int mid = (left + right) >> 1;        vector<int> v;        v.resize(M, 0);        for (int i = 0; i < mid; ++i) {            ++v[C[i]];        }        for (int i = 1; i < M; ++i) {            v[i] += v[i - 1];        }        bool can = true;        for (int i = 0; i < A.size(); ++i) {            if (v[B[i]] - v[A[i] - 1] == 0) {                can = false;                break;            }        }        if (can) {            result = mid;            right = mid - 1;        }        else {            left = mid + 1;        }    }    return result;}

更快的演算法,如果我們建立一個長度為2 * M的數組,每個位置表示該位置上釘子的最小編號(可能同一個位置有多個釘子,取編號最小的),沒有釘子的位置值為無窮大。那麼固定第i塊木板的最小編號釘子,相當於[A[i],B[i]]區間的最小值。但是我們這個題實際上是求這些最小值的max,首先如果一個木板A的覆蓋區間完全包含另外一個木板B,則實際上我們只考慮木板B即可。因為固定木板B同時能固定木板A,並且我們一定要固定木板B,即使A覆蓋區間有更小的值,也無法改變最終取最大值的結果。

於是,我們可以建立一個數組plank[x]表示右端點為x的木板的最大左邊界,沒有木板的話,認為邊界是0。我們從左至右遍曆木板右邊界,假設這之前(更左)的木板已經被固定了,已經固定的區間範圍是[left,right) (右開區間),然後對當前這個木板,如果顯然它的右邊界更大(我們遍曆右邊界是按當增的順序),如果該木板start <= left,則它已經被前面固定住了,不影響結果。否則,要求[start,end]之間的最大值。這個問題有點像滑動視窗最大值的問題。本質在於:我們不斷查詢最大值,每次查詢的時候視窗的左邊界和右有邊界是單調遞增的,於是我們可以動態更新視窗維護最大值。這個經典問題可以用單調隊列實現,這也是把單調隊列發揮到了極致。

結論: 查詢時段最大值的時候,如果視窗向右滑動的過程中,查詢時左邊界和右邊界都是單增的,則可以使用單調隊列解決。

時間複雜度 O(N + M)達到了線性。

代碼:

// you can also use includes, for example:// #include <algorithm>#include <deque>const int inf = 2000000000;int solution(vector<int> &A, vector<int> &B, vector<int> &C) {    // write your code in C++98    int m = C.size(), M = (m << 1) | 1;    vector<int> nail(M, inf);    for (int i = m - 1; i >= 0; --i) {        nail[C[i]] = i;    }    vector<int> plank(M, 0);    for (int i = 0; i < A.size(); ++i) {        plank[B[i]] = max(plank[B[i]], A[i]);    }    int left = 0, right = 0, r = 0;    deque<int> q;    for (int i = 1; i < M; ++i) {        if (plank[i] > left) {            left = plank[i];            while ((!q.empty()) && (q.front() < left)) {                q.pop_front();            }            for (right = max(right, left); right <= i; ++right) {                while ((!q.empty()) && (nail[q.back()] >= nail[right])) {                    q.pop_back();                }                q.push_back(right);            }            r = max(r, nail[q.front()]);            if (r >= inf) {                return -1;            }        }    }    return r + 1;}



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.