您的位置:首页 > 其它

CodeForces 24D Broken Robot

2017-01-05 10:34 113 查看
题意:n*m的棋盘,一个机器人在(i,j)处,每次等概率地停在原地,向左移动一格,向右移动一格,向下移动一格(不能移出棋盘).求走到最后一行所需期望步数.n<=1000,m<=1000

一个看起来可以用来DP的顺序是永远只能从上面走到下面,但同一行之间的转移会出现环.如果n和m的范围稍微小一点,我们可以像SDOI走迷宫一题跑一个分层的高斯消元,但这个题的范围比较大,会超时,但这道题的背景暗示我们列出来的方程组会比较规则,我们不妨先把方程列出来看看有什么特点.

设F[i][j]为从第i行第j列走到最后一行的期望步数

则有

F[i][1]=1+F[i][1]/3+F[i][2]/3+F[i+1]/3

F[i][j]=1+F[i][j]/4+F[i][j-1]/4+F[i][j+1]/4+F[i+1][j]/4,2<=j<=m-1

F[i][m]=1+F[i][m-1]/3+F[i+1][m]/3+F[i][m]/3

不妨仍考虑从第i+1行推到第i行.那么我们在求解F[i][1…m]的时候应当已经知道F[i+1][1…m],边界显然是F
[j]=0

对于一行,有两个方程只有两个未知数,m-2个方程有三个未知数

如果两个方程里面是同两个未知数,我们可以直接解出这两个未知数,但现在F[i][1]和F[i][m]的未知数并不一定是同两个,我们可以考虑对这些式子进行变形.

F[i][1]的方程和F[i][1],F[i][2]有关,F[i][2]的方程和F[i][1],F[i][2],F[i][3]有关,那么我们可以用这两个方程消掉F[i][1],得到关于F[i][2]和F[i][3]的二元一次方程,接下来再和F[i][3]的方程相消,可以得到关于F[i][3]和F[i][4]的方程….最终我们就可以解出最右端的F[i][m],此后顺着推回来,复杂度O(n).

实现的时候我把每个二元一次方程表示成F[i][j]=a[j]*F[i][j+1]+b[j]的形式,比较容易理解.

转移的式子可能需要简单推一下,见代码.

注意m=1的情况要特判.

#include<cstdio>
double f[1005][1005];
double a[1005],b[1005];
int main(){
int n,m,x0,y0;scanf("%d%d%d%d",&n,&m,&x0,&y0);
if(m==1){
a
=0;
for(int i=n-1;i>=x0;--i){
a[i]=a[i+1]+2;
}
printf("%.4f\n",a[x0]);
}else{
for(int i=1;i<=m;++i)f
[i]=0;
for(int i=n-1;i>=x0;--i){
a[1]=0.5;b[1]=f[i+1][1]/2+1.5;
for(int j=2;j<m;++j){
b[j]=b[j-1]/4.0+f[i+1][j]/4.0+1.0;
a[j]=0.25;
a[j]/=(0.75-a[j-1]/4.0);b[j]/=(0.75-a[j-1]/4.0);
}
f[i][m]=(b[m-1]+f[i+1][m]+3.0)/(2-a[m-1]);
for(int j=m-1;j>=1;--j)f[i][j]=b[j]+a[j]*f[i][j+1];
}
printf("%.4f\n",f[x0][y0]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: