動態規劃面試題--數字轉化機

最近編程題目都是DP,我把今天這個題給大家做個解釋和實現。大家好好體會練習,遇到最優解問題(最小/最大)時,優先DP解法。

概念

一般求最優解通常使用DP(Dynamic Programming)。

DP主要兩個要素,有三種解法。

  • 兩要素
    • 定義狀態
    • 定義狀態轉化方程

狀態和狀態轉化方程最關鍵也比較困難。狀態轉化方程包含邊界。有時稱為三要素,是把邊界單獨分離出來,與狀態和狀態轉化方程并列。

  • 三種解法
    • 遞歸法
      遞歸法是直接使用狀態轉化方程,實際算不上DP,而是減而治之,在重疊子問題時,效率低下(時間復雜度O(2^n))。
    • 備忘錄法
      備忘錄法實際上是對遞歸法優化,通過增加存儲空間保存中間運算值,減少遞歸運算次數。
    • 表格法
      這是最正統的DP,遞歸法和備忘錄法都是自頂而下的遞歸,表格法是自底而上的迭代。DP本質是把遞歸變為迭代,降低時間復雜度。

題目

時間限制:(每個case)2s,空間限制:128MB
小Q從牛博士那里獲得一臺數字轉化機,這臺數字轉化機必須同時輸入兩個整數ab,并且這臺數字轉化機有一個紅色按鈕和一個藍色按鈕。

  • 當按下紅色按鈕,兩個數字同時加一。
  • 當按下藍色按鈕,兩個數字同時乘二。

小Q現在手中有四個整數,abAB,他希望將輸入的兩個整數a、b變成A、Ba對應Ab對應B)。因為牛博士允許小Q使用數字轉化機時間有限,所以,小Q希望按動按鈕的次數越少越好,請你幫幫小Q吧。

  • 輸入:
    輸入包括一行,一行中包含四個整數a,b,A,B。(1<=a,b,A,B<=10^9)
  • 輸出:
    如果小Q能夠完成轉換,輸出最少需要按動按鈕的次數,否則輸出-1。
  • 樣例輸入:
100 1000 202 2002
  • 樣例輸出:
2

分析

  • 定義狀態
    transfer(first,last)表示從數字first轉化到數字last最少轉化次數
  • 定義狀態轉移方程
    自頂而下分析,最后一步轉化無非兩種情況,前一步結果*2或者+1,那么前一次結果為last/2或者last-1。最后一步經歷的轉化次數無非是transfer(first,last/2)或者transfer(first,last-1)。顯然可得,取最小的transfer(first,last) = min(transfer(first,last/2),transfer(first,last-1))+1
    遞歸邊界最終會有兩種情況,一種是first剛好與last相同,說明這是可以轉換。一種是first>last,說明不能完成轉化。
    結合上面兩點狀態轉移方程如下所示:

解決

為了使代碼更加簡潔地表示解決思路,中間未添加參數檢查處理。

測試主函數

本題測試函數非常簡單只是終端輸入輸出操作。

int main(){
    int a(0),b(0),A(0),B(0);
    cin >> a >> b >> A >> B;
    int res1 = transfer(a,A);
    int res2 = transfer(b,B);
    if(res1 == res2 and -1 != res1){
        cout << res1;
    }else{
        cout << -1;
    }
}

實現轉換函數transfer()

  • 遞歸法
    直接把狀態轉換函數翻譯成代碼即可。
int transfer(int first,int last){
    if(first == last) return 0; // 可以完成轉換
    if(first > last) return -1; // 不能完成轉換
    int res1 = transfer(first,last/2);
    int res2 = transfer(first,last-1);
    if(-1 == res1 and -1 == res2){
        return -1;
    }else if(-1 == res1){
        return res2+1;
    }else if(-1 == res2){
        return res1+1;
    }else{
        return min(res1,res2)+1;
    }
}

注意,轉化失敗的分支不能參與最小轉化次數比較的。程序需要增加這些處理。

  • 備忘錄法
    在遞歸過程中,記錄計算結果,下次出現時不需要重新計算,直接從存儲結果中獲取即可。
int transfer(int first,int last,int buf[]){
    if(first == last) return 0; // 可以完成轉換
    if(first > last) return -1; // 不能完成轉換
    if(-1 != buf[last]){
        return buf[last];
    }
    int res1 = transfer(first,last/2,buf);
    int res2 = transfer(first,last-1,buf);
    if(-1 == res1 and -1 == res2){
        return -1;
    }else if(-1 == res1){
        buf[last] = res2+1;
    }else if(-1 == res2){
        buf[last] = res1+1;
    }else{
        buf[last] = min(res1,res2)+1;
    }
    return buf[last];
}
int transfer(int first,int last){
    int buf[last+1];
    fill_n(buf,last+1,-1);
    return transfer(first,last,buf);
}

注意這里定義一個數組,數組的下標表示last,數組值表示從數字first轉化到數字last最少轉化次數。數組需要初始化為無效值-1,用于判斷是否已經計算過。

  • 表格法
    表格法用自底而上的迭代代替自頂而下的遞歸。
int transfer(int first,int last){
    int buf[last+1];
    fill_n(buf,last+1,0);
    for(int i=first+1;i<=last;i++){
        if(i/2 >= first and i-1 >= first){
            buf[i] = min(buf[i/2],buf[i-1])+1;
        }else if(i/2 >= first){
            buf[i] = buf[i/2]+1;
        }else if(i-1 >= first){
            buf[i] = buf[i-1]+1;
        }else{
            buf[i] = -1;
        }
    }
    return buf[last];
}

這里數組表示含義與備忘錄法一致的,雖然會有一定空間浪費,時間復雜度,已經從O(2^n)降低到O(n)

通常有時間限制的DP,優先使用表格法,備忘錄法在一些情況會比表格法快,基本不使用遞歸法。

使用回溯法可以把表格法和備忘錄法獲得的存儲中間值,轉化成獲得最優解具體的操作,例如本題中,小Q究竟是依次按了哪些按鈕獲得last值。有能力的童鞋,可以自己練習解決。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,764評論 0 33
  • 動態規劃(Dynamic Programming) 本文包括: 動態規劃定義 狀態轉移方程 動態規劃算法步驟 最長...
    廖少少閱讀 3,320評論 0 18
  • 回溯算法 回溯法:也稱為試探法,它并不考慮問題規模的大小,而是從問題的最明顯的最小規模開始逐步求解出可能的答案,并...
    fredal閱讀 13,708評論 0 89
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • 矮個子女生也能穿長裙嗎? 不要再給自己貼標簽了! 矮個姑娘穿長裙同樣HOLD得住。 夏天快到了,穿裙子的那顆心開始...
    拍范閱讀 404評論 0 0