cf(448c)-painting fence

這是一道面試題,同時是codeforces上的c類題目。
tags:divide and conquer,dp,greedy

解決思路

首先明確
  • 先需要明確每次只可能存在兩種刷法,要麼橫着一筆,要麼豎着一筆。
  • 豎着刷不會存在“斷開”的問題,而橫着刷會存在。
思考過程
step1

因爲存在橫向斷開的問題,也就意味着可能需要分連續橫向區間處理,那麼首先應該想到用遞歸,每一層遞歸找到當前木材的最低高度,比這個高度更大的木材進入下一層遞歸,連續大於最低高度的木材爲同一個遞歸。看題目給的數據範圍,最多5000塊木板,那麼如果每一層遞歸僅僅只少1塊木材,遞歸深度也不會超過5000,這對於gcc或者g++編譯器來說是可以容忍的。

step2(建議看了代碼再來看這一部分)

實在想不出有什麼規律,那麼我們假設所有木材全部豎着刷,然後與所有木材橫着刷做比較,誰小誰就是結果。但是問題來了,橫着刷存在斷開的問題,不能一次性橫向全部刷完,而且可能會存在在橫着刷完後後面全部豎着刷可能次數更少。那就用遞歸,我們就找所有的木材中最小的高度,最小高度即爲橫向刷牆需要的次數,接着加上剩下牆刷的次數,就爲橫向刷牆的次數。然後把最小高度作爲分界線,得到多個連續的更高高度的新牆,然後對這些子問題進行上述相同處理。

最終思路

最後的最小次數一定小於等於全部豎着刷牆,對於橫着刷牆的情況,只有把0-最小高度刷完纔可能比全部豎着刷牆更小(橫向刷的範圍不能更高,也不能更小,只能等於最小高度纔可能取得最優解,至於爲什麼,也很好證明),所以我們需要討論這種情況,這就有了分治的思想。刷完以後,我們把“基線”移到最小高度之上,這樣就得到了多個連續的規模更小的新牆,這是與願問題相同的子問題,我們需要求子問題的最優解願問題的最優解就包含了子問題的最優解(最優子結構)當前的狀態又是由橫向刷和豎向刷兩種情況中的最小值決定的。所以這道題就有了貪心、分治、動態規劃思想在裏面。

代碼

#include <iostream>
#include <limits.h>
#include <algorithm>
using namespace std;
//max re-deep is 5000,is ok 
int dfs(int* a,int l,int r,int h)
{
    if(l == r) 
        return 1;
    int min_h = *min_element(a+l,a+r+1);
    int res = min_h - h;
    for(int i=l;i<=r;i++)
    {
        if(a[i] > min_h)
        {
            int j = i;
            while(j < r && a[j+1] > min_h ) j++;
            res += dfs(a,i,j,min_h);
            i = j;
        }
    }
    return min(res,r-l+1);
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i = 0;i<n;i++)
    {
        cin >> arr[i];
    }
    cout << dfs(arr,0,n-1,0);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章