問題:
一個小島,表示爲一個N×N的方格,從(0,0)到(N-1, N-1),一個人站在島上,位置(x, y),他可以上下左右走,一步一個格子,他選擇上下左右的可能性是一樣的。當他走出小島,就意味着死亡。假設他要走k步,請問他死亡的概率有多大?
分析I:
要求概率,首先想到計算出所有路徑的種數N,以及出界的路徑種數M,死亡的概率就是N/M了。
計算路徑種數可以用DP的思想:
設走k步從位置(x, y)到達(i,j)的路徑種數爲f[k][i][j],那麼:
f[k][i][j] = f[k-1][i-1][j]+f[k-1][i+1][j] +f[k-1][i][j-1] +f[k-1][i][j+1] 。
所以很快寫出如下的代碼:
double ProbabilityAndCount(int x0, int y0, int N, int k, int &innerCount, int &outerCount){
static const int directons[4][2]={{0,1}, {0,-1}, {1, 0}, {-1, 0}};
innerCount = 0;
outerCount = 0;
if(N == 1)
return 1.0;
vector<vector<int>>memos(2, vector<int>(N * N, 0));
int memoIndex = 0;
memos[memoIndex][y0 * N + x0] = 1;
for(int stepi = 1; stepi <= k; ++stepi){
int preMemoIndex = memoIndex;
memoIndex = 1 - memoIndex;
for(int y = 0; y < N; ++y){
for(int x = 0; x < N; ++x){
//更新正好第stepi步走出界的路線種數
if(y == 0 || y == N - 1)
outerCount += memos[preMemoIndex][y * N + x];
if(x == 0 || x == N - 1)
outerCount += memos[preMemoIndex][y * N + x];
//更新走stepi步後,走到(x, y)位置處的路線種數
memos[memoIndex][y * N + x] = 0;
for(int dir = 0; dir < 4; ++dir){//遍歷4個鄰居
int nx = x + directons[dir][0];
int ny = y + directons[dir][1];
if(nx >= 0 && ny >= 0 && nx < N && ny < N)
memos[memoIndex][y * N + x] += memos[preMemoIndex][ny * N + nx];
}
}
}
}
innerCount = std::accumulate(memos[memoIndex].begin(), memos[memoIndex].end(), 0);
return (double)outerCount / (outerCount + innerCount);//返回走出界的概率
}
然而,上面的代碼是不正確的 !
代碼中出界的路徑長度是不一樣的,所以出界路徑的概率並不一樣,不能夠用路徑總數來計算出界概率!
解決辦法:
每一步的概率都是0.25,那麼長度爲k的路徑的概率就爲0.25^k,將所有出界的路徑的概率加起來即可:
double Probability(int x0, int y0, int N, int k){
static const int directons[4][2]={{0,1}, {0,-1}, {1, 0}, {-1, 0}};
if(N == 1)
return 1.0;
double res = 0;
vector<vector<int>>memos(2, vector<int>(N * N, 0));
int memoIndex = 0;
memos[memoIndex][y0 * N + x0] = 1;
double probalityBase = 0.25;
for(int stepi = 1; stepi <= k; ++stepi, probalityBase *= 0.25){
int preMemoIndex = memoIndex;
memoIndex = 1 - memoIndex;
for(int y = 0; y < N; ++y){
for(int x = 0; x < N; ++x){
//更新正好走出界的概率(加上第stepi步走出界的概率)
if(y == 0 || y == N - 1)
res += probalityBase * memos[preMemoIndex][y * N + x];
if(x == 0 || x == N - 1)
res += probalityBase * memos[preMemoIndex][y * N + x];
//更新走stepi步後,走到(x, y)位置處的路線種數
memos[memoIndex][y * N + x] = 0;
for(int dir = 0; dir < 4; ++dir){//遍歷4個鄰居
int nx = x + directons[dir][0];
int ny = y + directons[dir][1];
if(nx >= 0 && ny >= 0 && nx < N && ny < N)
memos[memoIndex][y * N + x] += memos[preMemoIndex][ny * N + nx];
}
}
}
}
return res;
}
分析II:
實際我們可以不從路徑種數入手,直接從概率入手,仍然是DP思想:
設從(i,j)走k步出界的概率爲f[k][i][j],那麼:
f[k][i][j] = (f[k-1][i-1][j]+f[k-1][i+1][j] +f[k-1][i][j-1] +f[k-1][i][j+1]) * 0.25 ,
若i,j已經出界,那麼f[...][i][j] =1;
具體實現:時間複雜度O(k*N*N),空間複雜度O(N*N)
double Probability2(int x0, int y0, int N, int k){
static const int directons[4][2]={{0,1}, {0,-1}, {1, 0}, {-1, 0}};
vector<vector<double>>memos(2, vector<double>(N * N, 0));
int memoIndex = 0;
for(int stepi = 1; stepi <= k; ++stepi){
int preMemoIndex = memoIndex;
memoIndex = 1 - memoIndex;
for(int y = 0; y < N; ++y){
for(int x = 0; x < N; ++x){
memos[memoIndex][y * N + x] = 0;
for(int dir = 0; dir < 4; ++dir){//遍歷4個鄰居
int nx = x + directons[dir][0];
int ny = y + directons[dir][1];
memos[memoIndex][y * N + x] += (nx >= 0 && ny >= 0 && nx < N && ny < N ? memos[preMemoIndex][ny * N + nx] : 1) * 0.25;
}
}
}
}
return memos[memoIndex][y0 * N + x0];
}