• @topcoder



    @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 那道高消的优化思路有点类似(用合并方程的思想逐渐把未知量消掉)。。。
    不过也可能是我联想能力太强。。。

  • 相关阅读:
    判断ascii码是什么的函数
    php curl
    js form settimeout
    windows php文件下载地址
    面试(3)
    [读码时间] 显示单击的坐标
    [读码时间] 模拟select控件
    [读码时间] 星级评分
    [读码时间] 事件练习:封闭兼容性添加,删除事件的函数
    [读码时间] 数组方法的使用
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12166945.html
Copyright © 2020-2023  润新知