【xsy1201】 随机游走 高斯消元

题目大意:你有一个$n*m$的网格(有边界),你从$(1,1)$开始随机游走,求走到$(n,m)$的期望步数。

数据范围:$n≤10$,$m≤1000$。

我们令 $f[i][j]$表示从$(1,1)$随机游走到$(i,j)$的期望步数。不难推出:

如果$(i,j)$与边界不想邻,则有 $f[i][j]=\frac{1}{4}(f[i-1][j]+f[i+1][j]+f[i][j-1]+f[i][j+1])+1$

如果$(i,j)$与边界相邻,但不在四个角,则把式子中的$\frac{1}{4}$改为$\frac{1}{3}$,并且将括号中的四个项删掉一个。

如果$(i,j)$在非起点的三个角上,则式子也显然。

显然这是一个$nm$元一次方程,我们可以考虑用高斯消元在$O(n^3m^3)$的时间内完成求解,这样子可以拿到$50$分的好成绩。

我们令$x_{(i-1)m+j}$来表示$f[i][j]$。

那么式子就变成了$x_i=\frac{1}{4}(x_{i-1}+x_{i+1}+x_{i+m}+x_{i-m})+1$

然后我们会发现,第$i$条式子只有$[i-m,i+m]$是有值的。

根据高斯消元的特征,第i条式子中包含$x_{[i-m,i)}$的项值会被消掉,那么实际上存在项的部分为$x_{[m,i+m]}$。

我们又发现,式子中包含$x_i$的,只可能第$i-m$条式子至第$i+m$条式子。

那么,我们在高斯消元时,并不需要把对所有式子进行处理,只需要处理第$i$条式子的后$m$条式子的第$i$项至第$i+m$项即可。

时间复杂度降低至$O(nm^3)$,你可以得到$80$分的好成绩。

考虑到$m$很大,依然无法求解,考虑到$n$很小,我们将$n$和$m$进行$swap$,然后再去求解即可。

时间复杂度降低至$O(n^3m)$。可以得到$100$分的好成绩。

 #include<bits/stdc++.h>
#define M 10005
#define ok(x,y) (1<=(x)&&(x)<=n&&1<=(y)&&(y)<=m)
#define ok2(x,y) (ok(x,y)&&(!(x==1&&y==1)))
#define D double
using namespace std; D *a[M];int n,m; D get(int i,int j){
D hh=;
if(ok(i-,j)) hh++;
if(ok(i+,j)) hh++;
if(ok(i,j+)) hh++;
if(ok(i,j-)) hh++;
return /hh;
} void newhh(int x){
int i=(x-)/m+,j=(x-)%m+;
a[x]=new D[n*m+];
memset(a[x],,sizeof(D)*(n*m+));
D hh=get(i,j);
if(ok2(i-,j)) a[x][x-m]=-hh;
if(ok2(i+,j)) a[x][x+m]=-hh;
if(ok2(i,j+)) a[x][x+]=-hh;
if(ok2(i,j-)) a[x][x-]=-hh;
a[x][x]=; a[x][n*m+]=;
} int Main(){
scanf("%d%d",&n,&m);
if(n==&&m==) {printf("0\n"); return ;}
if(m>n) swap(n,m);
for(int i=;i<=m+;i++) newhh(i);
for(int i=;i<n*m;i++){
for(int j=i+;j<=min(i+m,n*m);j++){
D hh=a[j][i]/a[i][i];
for(int k=i;k<=min(i+m,n*m);k++)
a[j][k]-=hh*a[i][k];
a[j][n*m+]-=hh*a[i][n*m+];
}
delete[] a[i];
if(i+m+<=n*m) newhh(i+m+);
}
D ans=a[n*m][n*m+]/a[n*m][n*m];
delete[] a[n*m];
printf("%.0lf\n",ans);
} int main(){
int cas; cin>>cas;
while(cas--) Main();
}
上一篇:Mac 配置Spark环境scala+python版本(Spark1.6.0)


下一篇:【BZOJ3143】【HNOI2013】游走 高斯消元