题目大意:你有一个$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$分的好成绩。
1 #include2 #define M 10005 3 #define ok(x,y) (1<=(x)&&(x)<=n&&1<=(y)&&(y)<=m) 4 #define ok2(x,y) (ok(x,y)&&(!(x==1&&y==1))) 5 #define D double 6 using namespace std; 7 8 D *a[M];int n,m; 9 10 D get(int i,int j){ 11 D hh=0; 12 if(ok(i-1,j)) hh++; 13 if(ok(i+1,j)) hh++; 14 if(ok(i,j+1)) hh++; 15 if(ok(i,j-1)) hh++; 16 return 1/hh; 17 } 18 19 void newhh(int x){ 20 int i=(x-1)/m+1,j=(x-1)%m+1; 21 a[x]=new D[n*m+2]; 22 memset(a[x],0,sizeof(D)*(n*m+2)); 23 D hh=get(i,j); 24 if(ok2(i-1,j)) a[x][x-m]=-hh; 25 if(ok2(i+1,j)) a[x][x+m]=-hh; 26 if(ok2(i,j+1)) a[x][x+1]=-hh; 27 if(ok2(i,j-1)) a[x][x-1]=-hh; 28 a[x][x]=1; a[x][n*m+1]=1; 29 } 30 31 int Main(){ 32 scanf("%d%d",&n,&m); 33 if(n==1&&m==1) {printf("0\n"); return 0;} 34 if(m>n) swap(n,m); 35 for(int i=2;i<=m+2;i++) newhh(i); 36 for(int i=2;i ){ 37 for(int j=i+1;j<=min(i+m,n*m);j++){ 38 D hh=a[j][i]/a[i][i]; 39 for(int k=i;k<=min(i+m,n*m);k++) 40 a[j][k]-=hh*a[i][k]; 41 a[j][n*m+1]-=hh*a[i][n*m+1]; 42 } 43 delete[] a[i]; 44 if(i+m+1<=n*m) newhh(i+m+1); 45 } 46 D ans=a[n*m][n*m+1]/a[n*m][n*m]; 47 delete[] a[n*m]; 48 printf("%.0lf\n",ans); 49 } 50 51 int main(){ 52 int cas; cin>>cas; 53 while(cas--) Main(); 54 }