LightOJ1151 Snakes and Ladders 高斯消元 期望dp

给出 1 1 1 100 100 100 100 100 100个格子排成一行, 到达一些格子时会瞬间转移, 每次投骰子来确定走的步数, 如果走出格子之外就重新投, 问走到 100 100 100号格子的期望步数.

如果每个格子瞬间转移只会向后的话, 就是普通的期望 d p dp dp—只须从后往前dp即可. 但是有些格子可能会往回跳, 所以不满足无后效性.
按照普通期望 d p dp dp的做法, d p [ i ] dp[i] dp[i]表示从 i i i这里开始走, 走到终点的期望步数. 可惜在当前情景下, 我们计算 i i i时可能会用到 1 1 1 i − 1 i-1 i1 d p dp dp值, 而计算 1 1 1 i − 1 i-1 i1的dp值又依赖于 i i i位置的 d p dp dp值, 这样不管我们先算哪个都无从下手, 明显要解方程了…
我们考虑高斯消元来解方程.
那么具体的方程是啥嘞

Ⅰ 最后一个格子走到目标点的期望: d p [ 100 ] = 0 dp[100]=0 dp[100]=0;
Ⅱ 如果一个格子会瞬间转移, 那他的期望当然就等于转移到的那个格子的期望: d p [ i ] = d p [ t o [ i ] ] dp[i]=dp[to[i]] dp[i]=dp[to[i]];
Ⅲ 剩下的格子当然是没有瞬间转移的, 但可能会在投骰子之后跳出边界.

对于情况Ⅲ, 我举一个例子, 通过这个例子就能够推出公式了.
不妨设最后5个格子分别为 A B C D E ABCDE ABCDE, 而我们现在在 A A A上 ,并且 A A A上不会瞬间转移. 显然我们投到5或6都会导致重新投, 1234 1234 1234都会直接走向 B C D E BCDE BCDE四种情况.
根据期望dp的常规套路, 我们很容易列出这样一个式子:
A = 1 6 ( B + 1 ) + 1 6 ( C + 1 ) + 1 6 ( D + 1 ) + 1 6 ( E + 1 ) + 2 6 ( A + 1 ) A=\frac{1}{6}(B+1)+\frac{1}{6}(C+1)+\frac{1}{6}(D+1)+\frac{1}{6}(E+1)+\frac{2}{6}(A+1) A=61(B+1)+61(C+1)+61(D+1)+61(E+1)+62(A+1)
简单化简就是这样:
A = B + C + D + E + 6 4 A=\frac{B+C+D+E+6}{4} A=4B+C+D+E+6
所以对于情况Ⅲ的格子 i i i:
d p [ i ] = ∑ b a c k + 6 c n t dp[i]=\frac{\sum{back}+6}{cnt} dp[i]=cntback+6, cnt就是后面格子的个数.

高斯消元的话, 先把未知项移到左边, 常数项放等式右边, 第i行第j列对应第i个式子第j个变量的系数, 最后一列放常数项.

参考

代码:

void init() {
    int _ = read(), caz = 0;
    while (_--) {
        int n = read();
        for (int i = 1; i <= 100; ++i) {
            to[i] = i;
        }
        for (int i = 1; i <= n; ++i) {
            int a = read(), b = read();
            to[a] = b;
        }
        fill(mat[0], mat[0] + 110 * 110, 0);
        mat[100][100] = 1;
        mat[100][101] = 0;
        for (int i = 1; i <= 99; ++i) {
            if (to[i] != i) {
                mat[i][i] = 1;
                mat[i][to[i]] = -1;
                mat[i][101] = 0;
            } else {
                int cnt = 0;
                for (int j = 1; j <= 6; ++j) {
                    if (i + j <= 100) {
                        cnt++;
                        mat[i][i + j] = -1;
                    }
                }
                mat[i][i] = cnt;
                mat[i][101] = 6;
            }
        }
        Gauss(101, 101);
        printf("Case %d: %.8lf\n", ++caz, x[1]);
    }
}

高斯消元:

int Gauss(int equ, int var) {
    int i, j, k;
    int max_r;
    int col;
    int ta, tb;
    int LCM;
    int temp;
    int free_index, free_num;
    memset(free_x, 1, sizeof(free_x));
    memset(x, 0, sizeof(x));
    for (k = col = 0; k < equ && col < var; ++k, ++col) {
        max_r = k;
        for (int i = k + 1; i < equ; ++i) {
            if (fabs(mat[i][col]) - fabs(mat[max_r][col]) > eps) {
                max_r = i;
            }
        }
        if (max_r != k) {
            for (int j = k; j < var + 1; ++j) {
                swap(mat[max_r][j], mat[k][j]);
            }
        }
        if (fabs(mat[k][col]) <= eps) {
            --k;
            continue;
        }
        for (int i = k + 1; i < equ; ++i) {
            if (fabs(mat[i][col]) <= eps) {
                continue;
            }
            double tmp = mat[i][col] / mat[k][col];
            for (int j = col; j < var + 1; ++j) {
                mat[i][j] -= mat[k][j] * tmp;
            }
        }
    }
    for (int i = k; i < equ; ++i) {
        if (fabs(mat[i][var]) > eps) {
            return 0;
        }
    }
    if (k < var) {
        for (int i = k - 1; i >= 0; --i) {
            free_num = 0;
            for (int j = 0; j < var; ++j) {
                if (fabs(mat[i][j]) > eps && free_x[j]) {
                    ++free_num;
                    free_index = j;
                }
            }
            if (free_num > 1) {
                continue;
            }
            double tmp = mat[i][var];
            for (int j = 0; j < var; ++j) {
                if (j != free_index && fabs(mat[i][j]) > eps) {
                    tmp -= mat[i][j] * x[j];
                }
            }
            free_x[free_index] = 0;
            x[free_index] = tmp / mat[i][free_index];
        }
        return var - k;
    }
    for (int i = var - 1; i >= 0; --i) {
        double tmp = mat[i][var];
        for (int j = i + 1; j < var; ++j) {
            if (fabs(mat[i][j]) > eps) {
                tmp -= x[j] * mat[i][j];
            }
        }
        x[i] = tmp / mat[i][i];
    }
    return 1;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值