@topcoder - SRM614D1L3@ TorusSailing


@description@

给定一个 N*M 的方格图,某人从 (0, 0) 出发想要走到 (goalX, goalY)。

假如该人在 (x, y),他会等概率地走向 ((x + 1) mod N, y) 或 (x, (y + 1) mod M)。

求到达终点的期望步数。

原题链接。

@solution@

显然可以列出期望的 dp 方程 dp[x][y] = (dp[(x+1) mod N][y] + dp[x][(y+1) mod M])/2 + 1。
发现要用高斯消元,而普通的高斯消元 O(N^6) 的复杂度太高,无法通过。

注意到我们可以先人工合并一些方程。
具体操作是,保留一些量作为高斯消元的变量(此处我们选择与 (goalX, goalY) 同行与同列的量),将其视作常量。
然后利用转移图的特殊性质(此处转移图是个网格图),将其他量用这些量表示出来。
我们可以从 (goalX - 1, goalY - 1) 从下往上,自右往左依次得到其他量用这些量表示出来的结果。

这一部分的复杂度是 O(N^3),之后的高斯消元复杂度也为 O(N^3),我们就可以通过该题了。

@accepted code@

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

class TorusSailing{
	private:
		#define MAXN (200)
		struct node{
			double a[MAXN], b; int cnt;
			node() {}
			node(int n) {
				cnt = n, b = 0;
				for(int i=0;i<n;i++)
					a[i] = 0;
			}
			friend node operator + (const node &x, const node &y) {
				node z(x.cnt); z.b = x.b + y.b;
				for(int i=0;i<x.cnt;i++) z.a[i] = x.a[i] + y.a[i];
				return z;
			}
			friend node operator + (const node &x, const double &k) {
				node z = x; z.b += k; 
				return z;
			}
			friend node operator / (const node &x, const double &k) {
				node z(x.cnt); z.b = x.b / k;
				for(int i=0;i<x.cnt;i++) z.a[i] = x.a[i]/k;
				return z;
			}
		}a[MAXN][MAXN];
		
		double A[MAXN][MAXN];
		void gauss(int n, int m) {
			int r = 0, c = 0;
			while( r < n && c < m ) {
				int mxr = r;
				for(int i=r+1;i<n;i++)
					if( fabs(A[i][c]) >= fabs(A[mxr][c]) )
						mxr = i;
				if( r != mxr ) {
					for(int j=c;j<m;j++)
						swap(A[r][j], A[mxr][j]);
				}
				if( A[r][c] ) {
					double k = A[r][c];
					for(int j=c;j<m;j++)
						A[r][j] /= k;
					for(int i=0;i<n;i++) {
						if( i == r ) continue;
						k = A[i][c];
						for(int j=c;j<m;j++)
							A[i][j] -= k*A[r][j];
					}
					r++;
				}
				c++;
			}
		}
	public:
		double expectedTime(int N, int M, int goalX, int goalY) {
			int K = (N - 1) + (M - 1);
			for(int i=0;i<N-1;i++)
				a[i][M-1] = node(K), a[i][M-1].a[i] = 1;
			for(int j=0;j<M-1;j++)
				a[N-1][j] = node(K), a[N-1][j].a[j+N-1] = 1;
			a[N-1][M-1] = node(K);
			for(int j=M-2;j>=0;j--)
				for(int i=N-2;i>=0;i--)
					a[i][j] = (a[i+1][j] + a[i][j+1]) / 2 + 1;
			for(int i=0;i<N-1;i++) {
				node b = (a[i][0] + a[i+1][M-1]) / 2 + 1;
				for(int j=0;j<K;j++) A[i][j] = -b.a[j];
				A[i][i]++, A[i][K] = b.b;
			}
			for(int j=0;j<M-1;j++) {
				node b = (a[0][j] + a[N-1][j+1]) / 2 + 1;
				for(int i=0;i<K;i++) A[j+N-1][i] = -b.a[i];
				A[j+N-1][j+N-1]++, A[j+N-1][K] = b.b;
			}
			gauss(K, K + 1);
			int sx = N - goalX - 1, sy = M - goalY - 1;
			double ans = a[sx][sy].b;
			for(int i=0;i<a[sx][sy].cnt;i++)
				ans += a[sx][sy].a[i] * A[i][K];
			return ans;
		}
};

@details@

事实上,这道题感觉和 PKUWC2018 那道高消的优化思路有点类似(用合并方程的思想逐渐把未知量消掉)。。。
不过也可能是我联想能力太强。。。

posted @ 2020-01-08 15:36  Tiw_Air_OAO  阅读(201)  评论(0编辑  收藏  举报