[提高组集训2021] 蚂蚁

一、题目

蚂蚁一开始在 \((0,0)\) 这个位置,平面大小为 \(n\times m\),每次蚂蚁可以向右走或者是向上走,从 \((n-1,i)\) 这个点向右走就会到达 \((0,i)\),从 \((i,m-1)\) 向上走就会到达 \((i,0)\),问走到 \((x,y)\) 的期望步数。

\(n,m\leq 100\)

二、解法

为了便于下面的叙述,我们把蚂蚁的行走改成向下走和向左走,设 \(dp[i][j]\) 表示从 \((i,j)\) 出发到达 \((0,0)\) 的期望步数,显然转移有环,需要 \(O(n^3m^3)\) 的高斯消元。

关键问题在于减少未知数的数量,因为环只在边界出现,其它地方都是有顺序的,我们可以只设第一行和第一列为未知数(设主元),剩下每个点的期望步数都可以用主元加系数表示出来(直接递推即可),然后在主元的这些位置可以列出 \(n+m-1\) 个方程,足以解出 \(n+m-1\) 个未知数。

时间复杂度 \(O((n+m)^3)\)

三、总结

主元法是处理转移环的套路,线性递推可以设少量主元直接递推,或者是设部分主元来减少未知数的数量。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define db double
#define eps 1e-12
const int M = 205;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k;db a[M][M];
struct node
{
	db w[M];
	node() {memset(w,0,sizeof w);}
	node operator + (const node &b) const
	{
		node r;
		for(int i=1;i<=k;i++)
			r.w[i]=w[i]+b.w[i];
		return r;
	}
	node operator / (const db &b) const
	{
		node r;
		for(int i=1;i<=k;i++)
			r.w[i]=w[i]/b;
		return r;
	}
	void print()
	{
		for(int i=1;i<=k;i++)
			printf("%.3f ",w[i]);
		puts("");
	}
}g[M][M];
db Abs(db x) {return x>0?x:-x;}
void gauss()
{
	for(int i=1;i<k;i++)
	{
		for(int j=i;j<k;j++)
			if(Abs(a[j][i])>eps)
			{
				swap(a[i],a[j]);
				break;
			}
		for(int j=1;j<k;j++)
		{
			if(i==j || Abs(a[j][i])<=eps) continue;
			db tmp=a[j][i]/a[i][i];
			for(int l=i;l<=k;l++)
				a[j][l]-=a[i][l]*tmp;
		}
	}
}
signed main()
{
	n=read();m=read();k=n+m;
	for(int i=1;i<=n;i++)
		g[i][1].w[i]=1;
	for(int i=2;i<=m;i++)
		g[1][i].w[i+n-1]=1;
	for(int i=2;i<=n;i++)
		for(int j=2;j<=m;j++)
		{
			g[i][j]=g[i-1][j]/2+g[i][j-1]/2;
			g[i][j].w[k]--;
		}
	a[1][1]=1;
	for(int i=2;i<=n;i++)
	{
		node t=g[(i-2+n)%n+1][1]/2+g[i][m]/2;
		t.w[k]--;
		for(int j=1;j<=k;j++)
			a[i][j]=t.w[j];
		a[i][i]--;
	}
	for(int i=2;i<=m;i++)
	{
		node t=g[n][i]/2+g[1][(i-2+m)%m+1]/2;
		t.w[k]--;int p=n+i-1;
		for(int j=1;j<=k;j++)
			a[p][j]=t.w[j];
		a[p][p]--;
	}
	gauss();
	db ans=0;int x=read()+1,y=read()+1;
	for(int i=1;i<k;i++)
		ans+=g[x][y].w[i]*a[i][k]/a[i][i];
	ans-=g[x][y].w[k];
	printf("%.9lf\n",ans);
}
posted @ 2021-09-27 22:00  C202044zxy  阅读(120)  评论(0编辑  收藏  举报