1. 程式人生 > >[lintcode] 1556.猜數遊戲 [Hard]

[lintcode] 1556.猜數遊戲 [Hard]

描述

為了進一步宣傳,Lintcode決定策劃一個猜數遊戲。 遊戲開始前,Lintcode會隨機保密地選擇一個不大於n的正整數x 讓玩家來猜,並提供一個長度為n的陣列aa[i]表示玩家選擇整數i時需要付出的金錢。 每一輪遊戲,玩家可以任意選擇一個整數並付出相應的價錢,如果猜中了,即選擇的數是x,則遊戲結束,否則Lintcode的小姐姐會告訴玩家x比玩家所選擇的數大或者小,隨後玩家繼續付錢選擇下一個數,直到猜中x為止。遊戲結束後,最後一輪猜數所付出的金錢a[x]將會被返還給玩家。 猜數的策略多種多樣,而同一種策略當x為不同的數時需要付出的金錢也不一樣。問能否找到一種最優策略,在最壞的情況下猜中數需要付出的代價最小?輸出最小的代價。

樣例

給出 a = [1,100,1],返回 2

解釋:
可以選擇的策略是:先猜1,如果沒中再猜3,如果還是沒中再猜2.
如果x = 1,則花費價錢 a[1] - a[1] = 0
如果x = 2,則花費價錢 a[1] + a[3] + a[2] - a[2] = 2
如果x = 3,則花費價錢 a[1] + a[3] - a[3] = 1
顯然這是一個最優策略,在最壞情況下只需要花費金錢2,故返回2

如果選擇其他的策略,比如先猜中間,再猜兩邊。當x = 1 或 3時,均需要付出金錢a[2] = 100
明顯最壞情況下需要花費更多的金錢,故不是一個最優的策略。`

給出 a = [1,3,5,1,1,8,4]

 , 返回 5

解釋:
此時的策略是先猜5,如果沒猜中則根據小姐姐反饋的x與5的大小關係繼續選擇:
如果反饋x < 5,則說明 1 <= x <= 4,此時可以先猜4,如果沒猜中再猜2,則可以保證最後能猜到x,最壞情況下的花費為:a[5] + a[4] + a[2] = 5
如果反饋x > 5,則說明 6 <= x <= 7,此時可以直接猜7,可以保證最後能猜到x,最壞情況下的花費為:a[5] + a[7] = 5
故返回 max{5,  5} = 5

思路

有一點博弈論的意思(max{min})。求的是最好的策略下最糟的情況。有一點繞, 也是一個經典的dp問題。

lintcode裡面hard的題目其實很多是1.思路清奇 2.邊界值特別麻煩。 程式碼量不見得高,但是很燒腦。

dp方程: dp[s,d] = min{max(i){dp[s,i-1], dp[i+1,d]} ) + a[i];

要注意一點是這個方程默認了兩件事:

1. 取的值一定在中間(這個其實不一定),所以有一個取第一個值時候的情況

2. s-d的長度至少為3

這兩點都很重要,需要考慮。

程式碼

class Solution {
public:
    /**
     * @param a: The array a
     * @return: Return the minimum cost
     */
    int min(int a, int b)
    {
        return a>b?b:a;
    }
    
    int max(int a, int b)
    {
        return a>b?a:b;
    }
     
    int getAnswer(vector<int> &a) {
        // Write your code here
        int n = a.size();
        int dp[201][201] = {0};
        
        for(int j=0; j<n-1; ++j)
        {
            dp[j][j+1] = a[j] < a[j+1]? a[j]:a[j+1];
        }
        

        for(int len = 3; len <= n ; ++len)
        {
            for(int start = 0; start < n-len+1; ++start)
            {
                int min=1<<29;
                
                int firstChoice = dp[start+1][start+len-1] + a[start];
                
                for(int i=start+1; i <= start+len-2; i++)
                {
                    int tmp = max(dp[start][i-1], dp[i+1][start+len-1]) + a[i];
                    
                    if(tmp < min)
                    {
                        min = tmp;
                    }
                }
                dp[start][start+len-1] = firstChoice < min? firstChoice:min;
            }
        }
        return dp[0][n-1];
    }
};