1. 程式人生 > >lintcode 骰子求和

lintcode 骰子求和

/* 
  今天被問到了一道lintcode上的題目,然而想了好久才想明白,看來在STL處折騰得太久,當初看《挑戰》時,粗淺地學的一點點動態規劃,已經忘得所剩無幾了...

  這幾天開始上課了,寫acm題的時間又被壓縮,不過還是盡力每天至少做一道演算法題,保持題感。
  此外,最近開始學java,偶爾也發些java的程式碼到部落格,由於剛入門java,程式碼會比較簡單,不過還是爭取每天保證相當的程式碼量,併發出來,激勵自己每天都要多花時間程式設計
*/
class Solution 
{
public:
    /**
     * @param n an integer
     * @return a list of pair<sum, probability>
     */
    vector<pair<int, double>> dicesSum(int n) 
	{
        // Write your code here
        vector<pair<int, double>> results;
        double f[n + 1][6 * n + 1];
        memset(f, 0, sizeof(f));

        for (int i = 1; i <= 6; ++i) f[1][i] = 1.0 / 6;
        
        for (int i = 2; i <= n; ++i) // 第 i 個骰子的點數和情況,其情況由前 (i-1) 個 骰子的點數和推出 
            for (int j = i; j <= 6 * i; ++j) // i 個骰子的點數和範圍,必定為 [i, 6i],全為1和全為6時,分別取得上下界 
			{
                for (int k = 1; k <= 6; ++k) // 第 n 個骰子的點數只可能為 1到6 
                    if (j > k) // j-k 必須嚴格大於0 (因為在這題中,由於i和j的實際意義分別為,投擲次數,和點數總和,所以兩者的最小值都不可能取到0) 
                        f[i][j] += f[i - 1][j - k]; //相應的,前(n-1)個骰子的點數和,就是 j-k,累加是為列舉所有能湊出 點數和為 j 的情況,並將出現概率求和 
                f[i][j] /= 6.0; // 在沒有除以6之前,表示的是在最後一次擲出k點的情況下,n 次投擲得到的點數和為 j 的概率 
            }
            
            /* 解釋下 f[i][j] /= 6.0; 這句程式碼;
			** 我覺得是用到了"條件概率公式",P(A) = P(A|B)* P(B)
			** 在 f[i][j] 還沒有除以6時,它的意思是,在滿足第n次擲出k點的情況下,n次總共透出點數和為j的概率,即為P(A|B)
			** 而擲出k的概率,由於點數 k 在 1~6之間等可能分佈,故為 1/6
			** 所以除以6以後,它才是真正的 P(A),其實本來該是在  f[i][j] += f[i - 1][j - k]; 句除以6,表示 "每種能湊出點數總和為 j 的情況出現的概率之和",但既然先只是累加求和,求完和除以6,也是一樣的,畢竟乘法有分配律 
            ** 
            */ 

        for (int i = n; i <= 6 * n; ++i) //n個骰子點數和範圍必為[n,6n],只要將對應的double陣列的元素組成pair壓棧返回即可
            results.push_back(make_pair(i, f[n][i]));

        return results;
    }
};