问题:
一个小岛,表示为一个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];
}